/**
   * Remove all references to the channel. 1) the map of a channel (socket) to its interests. 2) the
   * map of interests to its channels.
   *
   * <p>As each interest is removed inform all channels having a similar interest.
   *
   * @param sourceCtx
   */
  public void unregister(final ChannelHandlerContext sourceCtx) {
    final SocketAddress key = sourceCtx.channel().remoteAddress();
    majorLogger.info("unregister channel {}", key);
    final Interest.ChannelInterestSet channelInterestSet = this.channelInterestMap.remove(key);
    if (channelInterestSet == null) {
      majorLogger.warn(
          "nothing to unregister in interest map: {}", this.channelInterestMap.keySet());
      return;
    }
    final String trackingGuid = UUID.randomUUID().toString();
    majorLogger.warn("no interested channel set");

    for (final Interest interest : channelInterestSet.getInterestList()) {
      if (!this.interestedChannelMap.containsKey(interest)) {
        continue;
      }

      final Interest.InterestedChannelSet interestedChannelSet =
          this.interestedChannelMap.get(interest);
      interestedChannelSet.removeItem(sourceCtx, interest);
      majorLogger.info("unregistered interest {}", interest);
      // this.interestedChannelMap.put(interest, interestedChannelSet);

      // an implicit DISINTEREST message will be sent to all interested channels

      final MetaLinkMsg.Edit.Builder editBuilder =
          MetaLinkMsg.Edit.newBuilder()
              .addMode(MetaLinkMsg.Edit.EditMode.DISINTEREST)
              .setEditMode(MetaLinkMsg.Edit.EditMode.DISINTEREST)
              .setGuid(trackingGuid)
              .setSequence(sequenceNumber.incrementAndGet())
              .addAllTopic(interest.getTopic())
              .addOrigin(sourceCtx.channel().remoteAddress().toString())
              .addOrigin(METALINK_BRIDGE_NAME);
      final MetaLinkMsg.Edit disinterestMsg = editBuilder.build();

      majorLogger.trace("expressing disinterest to all channels except source");
      for (final Map.Entry<SocketAddress, Interest.ChannelInterestSet> entry :
          this.channelInterestMap.entrySet()) {
        final ChannelHandlerContext ctx = entry.getValue().context;
        if (isSameChannel(ctx, sourceCtx)) {
          continue;
        }
        majorLogger.trace("disinterest {} to {}", trackingGuid, ctx.channel().remoteAddress());
        ctx.write(disinterestMsg);
        ctx.flush();
        detailLogger.info("message sent:\n{}", disinterestMsg);
      }
    }
  }
  /**
   * Drop the interest for the specified socket.
   *
   * @param ctx
   * @param interest
   */
  public void setDisinterest(final ChannelHandlerContext ctx, final Interest interest) {
    majorLogger.info("set {} expression of disinterest {}:{}", ctx, interest);

    final SocketAddress key = ctx.channel().remoteAddress();
    if (this.channelInterestMap.containsKey(key)) {
      final Interest.ChannelInterestSet interestSet = this.channelInterestMap.get(key);
      interestSet.removeItem(ctx, interest);
      // this.channelInterestMap.put(key, interestSet);
    }
    if (this.interestedChannelMap.containsKey(interest)) {
      final Interest.InterestedChannelSet channelSet = this.interestedChannelMap.get(interest);
      channelSet.removeItem(ctx, interest);
      // this.interestedChannelMap.put(interest, channelSet);
    }
  }
  /**
   * Only registered channels may have interest set. Once interest has been set all post messages
   * with matching topic and will be sent to the channel.
   *
   * @param ctx the channel
   * @param interest the topic
   */
  public void setInterest(final ChannelHandlerContext ctx, final Interest interest) {
    majorLogger.info("set expression of interest [{}]", interest);

    final SocketAddress key = ctx.channel().remoteAddress();
    if (!this.channelInterestMap.containsKey(key)) {
      majorLogger.error("can not express interest from an unregistered client");
      // write a notice, to whom?
      return;
    }
    final Interest.ChannelInterestSet interestSet = this.channelInterestMap.get(key);
    interestSet.addItem(ctx, interest);

    final Interest.InterestedChannelSet channelSet;
    if ((!this.interestedChannelMap.containsKey(interest))
        || (this.interestedChannelMap.get(interest) == null)) {
      channelSet = new Interest.InterestedChannelSet(interest);
      this.interestedChannelMap.put(interest, channelSet);
    } else {
      channelSet = this.interestedChannelMap.get(interest);
    }
    channelSet.addItem(ctx, interest);
  }