from dht_common import SIZE_OF_HASH, SIZE_OF_IP_ADDRESS, SIZE_OF_KEY, SIZE_OF_SIGNATURE, SIZE_OF_TIMESTAMP
from base_node import Base_Node

class RCP_Quorum(Base_Node):
    def __init__(self, quorumID, documentSize, numNodes, numItems=0, table=[]):
        Base_Node.__init__(self, quorumID, documentSize, numItems, table)
        self.numNodes = numNodes

        self.nodeNumRounds = [0 for i in range(self.numNodes)]
        self.nodeNumMessagesSent = [0 for i in range(self.numNodes)]
        self.nodeNumMessagesRecv = [0 for i in range(self.numNodes)]
        self.nodeNumBytesSent = [0 for i in range(self.numNodes)]
        self.nodeNumBytesRecv = [0 for i in range(self.numNodes)]

        self.nodeLastNumRounds = [0 for i in range(self.numNodes)]
        self.nodeLastNumMessagesSent = [0 for i in range(self.numNodes)]
        self.nodeLastNumMessagesRecv = [0 for i in range(self.numNodes)]
        self.nodeLastNumBytesSent = [0 for i in range(self.numNodes)]
        self.nodeLastNumBytesRecv = [0 for i in range(self.numNodes)]

    def insert(self, numKeys, numSignatures):
        self.numItems += 1

        # Asker's ID
        sizeOfRequest = SIZE_OF_HASH
        # timestamp
        sizeOfRequest += SIZE_OF_TIMESTAMP
        # keys in request
        sizeOfRequest += SIZE_OF_KEY * numKeys
        # signatures in request
        sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
        # actual document sent
        sizeOfRequest += self.documentSize
        # signature over whole thing
        sizeOfRequest += SIZE_OF_SIGNATURE

        sizeOfResponse = SIZE_OF_HASH + SIZE_OF_SIGNATURE

        map(lambda x: x+1, self.nodeNumRounds)
        map(lambda x: x+1, self.nodeNumMessagesSent)
        map(lambda x: x+1, self.nodeNumMessagesRecv)
        map(lambda x: x+sizeOfResponse, self.nodeNumBytesSent)
        map(lambda x: x+sizeOfRequest, self.nodeNumBytesRecv)

    # This is simulating some extra stuff (signing) so that the correct size is recorded
    def retrieve(self, whichNode, numKeys, numSignatures):
        # Asker's ID
        sizeOfRequest = SIZE_OF_HASH
        # timestamp
        sizeOfRequest += SIZE_OF_TIMESTAMP
        # keys in request
        sizeOfRequest += SIZE_OF_KEY * numKeys
        # signatures in request
        sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
        # actual hash requested
        sizeOfRequest += SIZE_OF_HASH
        # signature over whole thing
        sizeOfRequest += SIZE_OF_SIGNATURE

        sizeOfResponse = self.documentSize + SIZE_OF_SIGNATURE

        self.nodeNumRounds[whichNode] += 1
        self.nodeNumMessagesSent[whichNode] += 1
        self.nodeNumMessagesRecv[whichNode] += 1
        self.nodeNumBytesSent[whichNode] += sizeOfResponse
        self.nodeNumBytesRecv[whichNode] += sizeOfRequest

    def get_finger_table_val(self, whichNode, searchID, numKeys, numSignatures):
        retval = Base_Node.get_finger_table_val(self, searchID)

        # key of previous group
        sizeOfResponse = SIZE_OF_KEY
        # next groups ID/routing information
        sizeOfResponse += SIZE_OF_IP_ADDRESS * self.numNodes + SIZE_OF_HASH
        # key of next group
        sizeOfResponse += SIZE_OF_KEY
        # sign the whole thing
        sizeOfResponse += SIZE_OF_SIGNATURE

        # Asker's ID
        sizeOfRequest = SIZE_OF_HASH
        # timestamp
        sizeOfRequest += SIZE_OF_TIMESTAMP
        # Keys in request
        sizeOfRequest += SIZE_OF_KEY * numKeys
        # signatures in request
        sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
        # ID being searched for
        sizeOfRequest += SIZE_OF_HASH

        self.nodeNumRounds[whichNode] += 1
        self.nodeNumMessagesSent[whichNode] += 1
        self.nodeNumMessagesRecv[whichNode] += 1
        self.nodeNumBytesSent[whichNode] += sizeOfResponse
        self.nodeNumBytesRecv[whichNode] += sizeOfRequest

        return retval

    def get_first_auth(self, whichNode):
        # Asker's ID
        sizeOfRequest = SIZE_OF_HASH
        # timestamp
        sizeOfRequest += SIZE_OF_TIMESTAMP

        # response is just the whole thing signed
        sizeOfResponse = sizeOfRequest + SIZE_OF_SIGNATURE

        self.nodeNumRounds[whichNode] += 1
        self.nodeNumMessagesSent[whichNode] += 1
        self.nodeNumMessagesRecv[whichNode] += 1
        self.nodeNumBytesSent[whichNode] += sizeOfResponse
        self.nodeNumBytesRecv[whichNode] += sizeOfRequest

        return None

    def get_num_nodes(self):
        return self.numNodes

    def get_num_rounds(self, whichNode):
        self.nodeLastNumRounds[whichNode] = self.nodeNumRounds[whichNode]
        return self.nodeNumRounds[whichNode]

    def get_recent_num_rounds(self, whichNode):
        retval = self.nodeNumRounds[whichNode] - self.nodeLastNumRounds[whichNode]
        self.nodeLastNumRounds[whichNode] = self.nodeNumRounds[whichNode]
        return retval

    def get_num_messages_sent(self, whichNode):
        self.nodeLastNumMessagesSent[whichNode] = self.nodeNumMessagesSent[whichNode]
        return self.nodeNumMessagesSent[whichNode]

    def get_recent_num_messages_sent(self, whichNode):
        retval = self.nodeNumMessagesSent[whichNode] - self.nodeLastNumMessagesSent[whichNode]
        self.nodeLastNumMessagesSent[whichNode] = self.nodeNumMessagesSent[whichNode]
        return retval

    def get_num_messages_recv(self, whichNode):
        self.nodeLastNumMessagesRecv[whichNode] = self.nodeNumMessagesRecv[whichNode]
        return self.nodeNumMessagesRecv[whichNode]

    def get_recent_num_messages_recv(self, whichNode):
        retval = self.nodeNumMessagesRecv[whichNode] - self.nodeLastNumMessagesRecv[whichNode]
        self.nodeLastNumMessagesRecv[whichNode] = self.nodeNumMessagesRecv[whichNode]
        return retval

    def get_num_bytes_sent(self, whichNode):
        self.nodeLastNumBytesSent[whichNode] = self.nodeNumBytesSent[whichNode]
        return self.nodeNumBytesSent[whichNode]

    def get_recent_num_bytes_sent(self, whichNode):
        retval = self.nodeNumBytesSent[whichNode] - self.nodeLastNumBytesSent[whichNode]
        self.nodeLastNumBytesSent[whichNode] = self.nodeNumBytesSent[whichNode]
        return retval

    def get_num_bytes_recv(self, whichNode):
        self.nodeLastNumBytesRecv[whichNode] = self.nodeNumBytesRecv[whichNode]
        return self.nodeNumBytesRecv[whichNode]

    def get_recent_num_bytes_recv(self, whichNode):
        retval = self.nodeNumBytesRecv[whichNode] - self.nodeLastNumBytesRecv[whichNode]
        self.nodeLastNumBytesRecv[whichNode] = self.nodeNumBytesRecv[whichNode]
        return retval

# TODO: Add unit tests for size calculations
# TODO: Add unit test to make sure finger_table_val is still firing correctly?
if __name__ == "__main__":
    SIZE_OF_DOCUMENTS_IN_TEST = 1024
    NUM_NODES_PER_QUORUM_IN_TEST = 10
    test = RCP_Quorum(0, SIZE_OF_DOCUMENTS_IN_TEST, NUM_NODES_PER_QUORUM_IN_TEST)
    
    randomNode = 0
    [test.insert() for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
    [test.retrieve(randomNode) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
    print("Insert and retrieval on node 0 fires correctly.")

    [test.retrieve(x) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
    print("Retrieval fires on all nodes correctly.")