private FindKeySuccessorResult FindPredecessorForId(FindKeySuccessorArg id)
        {
            var result = FindClosestPrecedingFinger(id.Id);
            Log.Write(LogEvent.Debug,
                    "FindSuccessorForId(from: {1}, for {2}): FindClosestPrecedingFinger returned node {0} and ...",
                    result.Id,
                    LocalNode.Id,
                    id
                );

            // stop recursion
            if (result.Equals(LocalNode)) {
                Log.Write(LogEvent.Debug,
                    "FindSuccessorForId(from: {0}, for {1}): ... actually it is the same as local node {0}, so return it",
                    LocalNode.Id,
                    id
                );
                return new FindKeySuccessorResult(LocalNode, false).CopyStatistics(id);
            }

            if (ChordOverlayHelper.IsInCircularInterval(id.Id, LocalNode.Id, LocalNode.Successor.Id, includeRight: true)) {
                Log.Write(LogEvent.Debug,
                    "FindSuccessorForId(from: {0}, for {1}): ... OverlayHelper.IsInCircularInterval({1}, {0}, {2}, includeRight: true) is true, so return it",
                    LocalNode.Id,
                    id,
                    LocalNode.Successor.Id
                );
                return new FindKeySuccessorResult(result, true).CopyStatistics(id);
            } else {
                var resultNodeSrv = CommunicationMgr.GetChannel(result);

                try {
                    Log.Write(LogEvent.Debug,
                        "FindSuccessorForId(from: {0}, for {1}): ... OverlayHelper.IsInCircularInterval({1}, {0}, {2}, includeRight: true) is false, so call FindSuccessorForId(from: {3}, for {1})",
                        LocalNode.Id,
                        id,
                        LocalNode.Successor.Id,
                        result.Id
                    );
                    // +1 hop, since we are calling the same method on the remote successor
                    id.StatsCollector.IncreaseHopsCount(1);
                    // if we call this recursively, we do not need to get the successor of the result inside
                    return resultNodeSrv.Service.FindKeySuccessor(new FindKeySuccessorArg(id.Id, false).CopyStatistics(id));
                } catch (Exception ex) {
                    if (resultNodeSrv.IsUnavailable) {
                        Log.Write(
                            LogEvent.Warn,
                            "Node {0} cannot propagate FindSuccessorForId for node {1} because the service is unaccessible, returning local node instead. Error details: \r\n {2}",
                            LocalNode,
                            result,
                            ex.ToString()
                        );

                        // TODO: correct?
                        return new FindKeySuccessorResult(LocalNode, false).CopyStatistics(id);
                    } else {
                        throw;
                    }
                }
            }
        }
        public FindKeySuccessorResult FindKeySuccessor(FindKeySuccessorArg key)
        {
            Log.Write(LogEvent.Debug, "FindSuccessorForId(from: {0}, for {1})", LocalNode.Id, key);

            // check if my successor is actually the successor of this id
            if (ChordOverlayHelper.IsInCircularInterval(key.Id, LocalNode.Id, LocalNode.Successor.Id, includeRight: true)) {
                Log.Write(LogEvent.Debug,
                    "FindSuccessorForId(from: {1}, for {0}): OverlayHelper.IsInCircularInterval({0}, {1}, {2}, includeRight: true) is true, returning LocalNode.Successor: {2}",
                    key, LocalNode.Id, LocalNode.Successor.Id
                );
                return new FindKeySuccessorResult(LocalNode.Successor, false).CopyStatistics(key);
            }

            // check if me is actually the successor of this id
            if (LocalNode.Predecessor != null && ChordOverlayHelper.IsInCircularInterval(key.Id, LocalNode.Predecessor.Id, LocalNode.Id, includeRight: true)) {
                Log.Write(LogEvent.Debug,
                    "FindSuccessorForId(from: {2}, for {0}): OverlayHelper.IsInCircularInterval({0}, {1}, {2}, includeRight: true) is true, returning LocalNode: {2}",
                    key, LocalNode.Predecessor.Id, LocalNode.Id
                );
                return new FindKeySuccessorResult(LocalNode, false).CopyStatistics(key);
            }

            var pred = FindPredecessorForId(key);

            var predSrv = CommunicationMgr.GetChannel(pred.Node);
            try {
                var returnSuccessorAsResult = pred.ReturnSuccessorAsResult && key.ReturnSuccessorAsResult;
                var res = returnSuccessorAsResult ? predSrv.Service.GetSuccessor() : pred.Node;
                if (returnSuccessorAsResult) {
                    key.StatsCollector.IncreaseHopsCount(1); // for GetSuccessor
                }
                Log.Write(LogEvent.Debug,
                    returnSuccessorAsResult
                        ? "FindSuccessorForId(from: {2}, for {3}): FindPredecessorForId returned node {0}, thus the result of FindSuccessorForId is {0}'s successor: {1}"
                        : "FindSuccessorForId(from: {2}, for {3}): FindPredecessorForId returned node {0}, thus the result of FindSuccessorForId is {0}",
                    pred.Node.Id,
             					res.Id,
                    LocalNode.Id,
                    key
                );
                return new FindKeySuccessorResult(res, false).CopyStatistics(key);
            } catch (Exception ex) {
                if (predSrv.IsUnavailable) {
                    Log.Write(
                        LogEvent.Warn,
                        "Node {0} cannot call GetNodeSuccessor for node {1} because the service is unaccessible, returning local node instead. Error details: \r\n {2}",
                        LocalNode,
                        pred,
                        ex.ToString()
                    );

                    // TODO: correct?
                    return new FindKeySuccessorResult(LocalNode, false).CopyStatistics(key);
                } else {
                    throw;
                }
            }
            throw new NotImplementedException();
        }