# Path: src/lifeRaft/states/leader.py
# class Leader(Voter):
# 
#     def __init__(self, timeout=1.0):
#         super(Leader, self).__init__(timeout=timeout)
#         self._nextIndexes = defaultdict(int)
#         self._matchIndex = defaultdict(int)
#         self._last_heartbeat = 0
# 
#     def set_server(self, server):
#         self._server = server
# 
#         for n in self._server._neighbors:
#             self._nextIndexes[n._name] = self._server._lastLogIndex + 1
#             self._matchIndex[n._name] = 0
# 
#     def on_append_entries(self, message):
#         if message.sender == self._server._name:
#             # bcast
#             return self, None
#         if message.term > self._server._currentTerm:
#             ## If we're getting append messages from the future, we're not the leader
#             from .follower import Follower
#             follower = Follower()
#             follower.set_server(self._server)
#             return follower, None
#         raise RuntimeError
# 
#     def on_vote_received(self, message):
#         ''' Well I find it dandy you're still voting for me '''
#         return self, None
# 
#     def on_response_received(self, message):
#         # Was the last AppendEntries good?
#         if(not message.data["response"]):
#             # No, so lets back up the log for this node
#             self._nextIndexes[message.sender] -= 1
# 
#             # Get the next log entry to send to the client.
#             previousIndex = max(0, self._nextIndexes[message.sender] - 1)
#             previous = self._server._log[previousIndex]
#             current = self._server._log[self._nextIndexes[message.sender]]
# 
#             # Send the new log to the client and wait for it to respond.
#             appendEntry = AppendEntriesMessage(
#                 self._server._name,
#                 message.sender,
#                 self._server._currentTerm,
#                 {
#                     "leaderId": self._server._name,
#                     "prevLogIndex": previousIndex,
#                     "prevLogTerm": previous["term"],
#                     "entries": [current],
#                     "leaderCommit": self._server._commitIndex,
#                 })
# 
#             self._send_response_message(appendEntry)
#         else:
#             # The last append was good so increase their index.
#             self._nextIndexes[message.sender] += 1
# 
#             # Are they caught up?
#             if(self._nextIndexes[message.sender] > self._server._lastLogIndex):
#                 self._nextIndexes[message.sender] = self._server._lastLogIndex
# 
#         return self, None
# 
#     @property
#     def timeout(self):
#         ''' Nyquist rate on avg election timeout; bcast freq >= 3x timeout freq ==> no timeouts '''
#         return self._timeout * .75
# 
#     def on_leader_timeout(self):
#         ''' While leader, bcast heartbeat on timeout '''
#         self._send_heart_beat()
#         return self, None
# 
#     def _send_heart_beat(self):
#         message = AppendEntriesMessage(
#             self._server._name,
#             None,
#             self._server._currentTerm,
#             {
#                 "leaderId": self._server._name,
#                 "prevLogIndex": self._server._lastLogIndex,
#                 "prevLogTerm": self._server._lastLogTerm,
#                 "entries": [],
#                 "leaderCommit": self._server._commitIndex,
#             })
#         self._server.send_message(message)
# 
#     def on_client_message(self, message):
#         if message.command == ClientMessage.Leader:
#             return ClientLeaderResponse(message.sender, self._server._name, True)
#         else:
#             #TODO: Raft lol
#             raise NotImplemented
# 
# Path: src/lifeRaft/messages/client.py
# class ClientMessage(object):
#     # Types
#     Leader = 0
#     Append = 1
# 
#     def __init__(self, sender, receiver_host, receiver_port, command):
#         self._snd_id = sender
#         self._rcv_host = receiver_host
#         self._rcv_port = receiver_port
#         self._command = command
# 
#     @property
#     def command(self):
#         return self._command
# 
#     @property
#     def sender(self):
#         return self._snd_id
# 
#     @property
#     def receiver_host(self):
#         return self._receiver_host
# 
#     @property
#     def receiver_port(self):
#         return self._receiver_port


from ..states.leader import Leader
from ..messages.client import ClientMessage
import zmq
import threading
import pickle
import Queue
import time
import math
                        import time
    '''
    Simple mock up for creating cluster definitions of remote nodes
    (We don't want to actually set up a server)
    '''
    def __init__(self, name, host='127.0.0.1', port=6666, client_port=5000):
        self._name = name
        self._host = host
        self._port = port
        self._client_port = client_port

class ZeroMQClient(object):
    def __init__(self, neighbors, client_id='client', timeout=5):
        self._client_id = client_id
        self._neighbors = neighbors
        self._context = zmq.Context()
        self._context.setsockopt(zmq.LINGER, 0)
        self._timeout = 5

    @property
    def quorum(self):
        return math.ceil(0.5 * len(self._neighbors))

    @property
    def leader(self):
        ''' Sends a Leader request to all nodes, establishing leader by quorum '''
        votes = {}
        sockets = []
        for n in self._neighbors:
            sock = self._context.socket(zmq.REQ)
            sock.connect("tcp://%s:%d" % (n._host, n._client_port))

# Path: src/lifeRaft/states/leader.py
# class Leader(Voter):
# 
#     def __init__(self, timeout=1.0):
#         super(Leader, self).__init__(timeout=timeout)
#         self._nextIndexes = defaultdict(int)
#         self._matchIndex = defaultdict(int)
#         self._last_heartbeat = 0
# 
#     def set_server(self, server):
#         self._server = server
# 
#         for n in self._server._neighbors:
#             self._nextIndexes[n._name] = self._server._lastLogIndex + 1
#             self._matchIndex[n._name] = 0
# 
#     def on_append_entries(self, message):
#         if message.sender == self._server._name:
#             # bcast
#             return self, None
#         if message.term > self._server._currentTerm:
#             ## If we're getting append messages from the future, we're not the leader
#             from .follower import Follower
#             follower = Follower()
#             follower.set_server(self._server)
#             return follower, None
#         raise RuntimeError
# 
#     def on_vote_received(self, message):
#         ''' Well I find it dandy you're still voting for me '''
#         return self, None
# 
#     def on_response_received(self, message):
#         # Was the last AppendEntries good?
#         if(not message.data["response"]):
#             # No, so lets back up the log for this node
#             self._nextIndexes[message.sender] -= 1
# 
#             # Get the next log entry to send to the client.
#             previousIndex = max(0, self._nextIndexes[message.sender] - 1)
#             previous = self._server._log[previousIndex]
#             current = self._server._log[self._nextIndexes[message.sender]]
# 
#             # Send the new log to the client and wait for it to respond.
#             appendEntry = AppendEntriesMessage(
#                 self._server._name,
#                 message.sender,
#                 self._server._currentTerm,
#                 {
#                     "leaderId": self._server._name,
#                     "prevLogIndex": previousIndex,
#                     "prevLogTerm": previous["term"],
#                     "entries": [current],
#                     "leaderCommit": self._server._commitIndex,
#                 })
# 
#             self._send_response_message(appendEntry)
#         else:
#             # The last append was good so increase their index.
#             self._nextIndexes[message.sender] += 1
# 
#             # Are they caught up?
#             if(self._nextIndexes[message.sender] > self._server._lastLogIndex):
#                 self._nextIndexes[message.sender] = self._server._lastLogIndex
# 
#         return self, None
# 
#     @property
#     def timeout(self):
#         ''' Nyquist rate on avg election timeout; bcast freq >= 3x timeout freq ==> no timeouts '''
#         return self._timeout * .75
# 
#     def on_leader_timeout(self):
#         ''' While leader, bcast heartbeat on timeout '''
#         self._send_heart_beat()
#         return self, None
# 
#     def _send_heart_beat(self):
#         message = AppendEntriesMessage(
#             self._server._name,
#             None,
#             self._server._currentTerm,
#             {
#                 "leaderId": self._server._name,
#                 "prevLogIndex": self._server._lastLogIndex,
#                 "prevLogTerm": self._server._lastLogTerm,
#                 "entries": [],
#                 "leaderCommit": self._server._commitIndex,
#             })
#         self._server.send_message(message)
# 
#     def on_client_message(self, message):
#         if message.command == ClientMessage.Leader:
#             return ClientLeaderResponse(message.sender, self._server._name, True)
#         else:
#             #TODO: Raft lol
#             raise NotImplemented
# 
# Path: src/lifeRaft/messages/client.py
# class ClientMessage(object):
#     # Types
#     Leader = 0
#     Append = 1
# 
#     def __init__(self, sender, receiver_host, receiver_port, command):
#         self._snd_id = sender
#         self._rcv_host = receiver_host
#         self._rcv_port = receiver_port
#         self._command = command
# 
#     @property
#     def command(self):
#         return self._command
# 
#     @property
#     def sender(self):
#         return self._snd_id
# 
#     @property
#     def receiver_host(self):
#         return self._receiver_host
# 
#     @property
#     def receiver_port(self):
#         return self._receiver_port


# Path: src/lifeRaft/servers/server.py
from ..states.leader import Leader
from ..messages.client import ClientMessage
import zmq
import threading
import pickle
import Queue
import time
import math
                        import time

    '''
    Simple mock up for creating cluster definitions of remote nodes
    (We don't want to actually set up a server)
    '''
    def __init__(self, name, host='127.0.0.1', port=6666, client_port=5000):
        self._name = name
        self._host = host
        self._port = port
        self._client_port = client_port

class ZeroMQClient(object):
    def __init__(self, neighbors, client_id='client', timeout=5):
        self._client_id = client_id
        self._neighbors = neighbors
        self._context = zmq.Context()
        self._context.setsockopt(zmq.LINGER, 0)
        self._timeout = 5

    @property
    def quorum(self):
        return math.ceil(0.5 * len(self._neighbors))

    @property
    def leader(self):
        ''' Sends a Leader request to all nodes, establishing leader by quorum '''
        votes = {}
        sockets = []
        for n in self._neighbors:
            sock = self._context.socket(zmq.REQ)
            sock.connect("tcp://%s:%d" % (n._host, n._client_port))

            message = ClientMessage(self._client_id, n._host, n._client_port, ClientMessage.Leader)