import threading
import logging
import uuid
import events
import queue
import helpers
import socket
from config_wrapper import config
from critical_section_leaver import CriticalSectionLeaver
from painter import DrawingQueueEvent
from message_sender import MessageSender
from dummy_message_sender import DummyMessageSender
import json


logger = logging.getLogger(__name__)

class ModelThread(threading.Thread):
    def __init__(self, event_queue, paint_queue, time_offset, init_data=None, init_connection=None):
        super(ModelThread, self).__init__()

        # Queues
        self.event_queue = event_queue
        self.paint_queue = paint_queue

        #Event handling
        self.handlers = {}
        self.initialize_handlers()

        # Unique uuid identifying clients in the network
        self.uuid = uuid.uuid4().hex
        # Flag indicating weather we want to enter critical section when the token comes
        self.want_to_enter_critical_section = False
        # Information about critical section like the timestamp and client uuid which is in the section
        self.critical_section = None
        # Time offset between ntp server and local time
        self.time_offset = time_offset
        # Value of the last token we have received
        self.last_token = None

        # Initial board state
        self.board_state = [[0 for _ in range(config.getint('Tkinter', 'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]

        # If we are the first client
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                # If there is no next_next_hop init data in the response we are the second client so we set
                # next next hop as our address
                self.next_next_hop_info = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
            else:
                # If there are more thant two clients we set the value from the response
                self.next_next_hop_info = init_data['next_next_hop']

            # Address of our predecessor
            self.predecessor = init_connection.getsockname()

            # We initialize connection to our next hop and we start sending queue
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
              self.critical_section = init_data['critical_section_state']
              self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})



            # We signal that client has initialized properly
            init_connection.shutdown(1)
            init_connection.close()

        # We start a dummy message sender event which will create dummy messages to detect connection breaks
        self.dummy_message_sender = DummyMessageSender(self.event_queue, self.uuid)
        self.dummy_message_sender.start()


    def run(self):
        while True:
            (e) = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in (len(board_state)):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (board_state, 1)})


    def initialize_handlers(self):
        # Inner Handlers
        self.handlers['InnerNewClientRequestEvent'] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event

        # Outter handlers
        self.handlers['DrawingInformationEvent'] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    ############################################################################################
    #
    #                                      Inner Event handlers
    ############################################################################################
    def handle_inner_draw_information_event(self, event):
        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                  x,y = point
                  self.board_state[x][y] = color
            except IndexError as e:
                return

            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (points, color)})
            if (self.sending_queue):
                self.sending_queue.put(
                    events.DrawingInformationEvent(self.uuid, helpers.get_current_timestamp(), points, color))

        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({"type": DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers.get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))


    def inner_next_hop_broken_event(self, _):
        # If we detect that the next hop connection is down we want to:
        # 1.Try to reconnect to the client
        # 2.If reconnect fails we want to connect to our next next hop
        # 3.When we succesfully connect to our next next hop we want to send recovery token question
        #   in case that the dead client was holding the token the moment he died
        ip, port = self.next_next_hop_info
        # If we are the only client left we reset the data to the initial state
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                # After we connect to a new client we have to check whether the dead client wasn't in posession
                # of token
                self.sending_queue.put(events.TokenReceivedQuestionEvent(self.last_token))
            except Exception as e:
                logger.error(e)

        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    ############################################################################################
    #
    #                                      Event handlers
    ############################################################################################
    def handle_new_client_request(self, event):
        # At first we want to receive information to properly connect as new predecessor after sending init_data
        message_size =  event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet =  event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))

        first_client = not self.next_hop_info
        # When we detect a new client connecting we want to;
        # 1.Send him the initial data over the connection we already established
        # 2.Connect to him as a predecessor

        # Gather the initial board state (only the coloured spots)
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        # If we have next hop information we send it, if we do not have we are the first client so we send our
        # information as the first hop information
        next_hop = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port')) if first_client else self.next_hop_info
        # If we are the first client next next hop is None
        response = events.NewClientResponseEvent(next_hop, self.next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = (len(message)).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)


        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                # Only case when we have a succesfull read of 0 bytes is when other socket shutdowns normally
                pass
            else:
                logger.error(ex)
                #Client did not initializ correctly so we abort the process
                return
        # If we are not the first client we have to update our next next hop to our previous next hop
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            # If we are the first client we update our next next hop info to self address
            self.next_next_hop_info = (
                helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port')
            )

        # We stop current message sender if it exists
        if self.message_sender:
            self.message_sender.stop()


        # We update our next hop info with the newest client request
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        # We establish a new connection and a new message sender
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            # If we are the first client we start passing of the token
            self.sending_queue.put(events.TokenPassEvent(self.last_token))


    def handle_drawing_information_event(self, event):
        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x,y = point
                    self.board_state[x][y] = color
            except IndexError:
                return

            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)

        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        # The moment we have a new predecessor this means that the client before our predecessor
        # has a new next next hop address (which is our address) and our predecessor has new next next hop (which is
        # our next hop)
        self.predecessor = event.data['client_address']
        self_address = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
        # Special case if we have only 2 nodes left
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor, self_address))
            self.next_next_hop_info = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
        else:
            # We send information to predecessor of our predecessor about his new next next hop address
            self.sending_queue.put(events.NewNextNextHop(self_address, self.predecessor))
            # We send information to our predecessor about his new next next hop
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info, self_address))

    def handle_entering_critical_section(self, event):
        data = event.data
        if (data['client_uuid']) == self.uuid:
            return
        self.critical_section = {
            'timestamp': data['timestamp'],
            'client_uuid': data['client_uuid']
        }
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
        self.sending_queue.put(event)

    def handle_leaving_critical_section(self, event):
        data = event.data
        if (data['client_uuid']) == self.uuid:
            return

        if self.critical_section and self.critical_section['client_uuid'] == event.data['client_uuid']:
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            self.critical_section = None

        self.sending_queue.put(event)

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token

        if self.critical_section:
            # If we have received the token and the critical section exists we unvalidate critical secion info
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})

        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {
                'timestamp': timestamp,
                'client_uuid': self.uuid
            }
            leave_critical_section_deamon = CriticalSectionLeaver(self.event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(timestamp, self.uuid))
        else:
            if self.sending_queue:
                self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info

        # We are the recipient of the message
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        # We check weather the last token we received is greater than
        # the token from the request
        # If it is, this means that the disconnected client was not in posession of the token when he disconnected
        # If it was we have to unvalidate critial secion information and send token further

        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        else:
            if self.sending_queue:
                self.sending_queue.put(event)

2b55f0eaacd01cbbb313c759591ef596cdeb1548
<mask token>


class ModelThread(threading.Thread):

    def __init__(self, event_queue, paint_queue, time_offset, init_data=
        None, init_connection=None):
        super(ModelThread, self).__init__()
        self.event_queue = event_queue
        self.paint_queue = paint_queue
        self.handlers = {}
        self.initialize_handlers()
        self.uuid = uuid.uuid4().hex
        self.want_to_enter_critical_section = False
        self.critical_section = None
        self.time_offset = time_offset
        self.last_token = None
        self.board_state = [[(0) for _ in range(config.getint('Tkinter',
            'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                self.next_next_hop_info = helpers.get_self_ip_address(
                    ), config.getint('NewPredecessorListener', 'Port')
            else:
                self.next_next_hop_info = init_data['next_next_hop']
            self.predecessor = init_connection.getsockname()
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
                self.critical_section = init_data['critical_section_state']
                self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
            init_connection.shutdown(1)
            init_connection.close()
        self.dummy_message_sender = DummyMessageSender(self.event_queue,
            self.uuid)
        self.dummy_message_sender.start()

    def run(self):
        while True:
            e = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in len(board_state):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (
            board_state, 1)})

    def initialize_handlers(self):
        self.handlers['InnerNewClientRequestEvent'
            ] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'
            ] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'
            ] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'
            ] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'
            ] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event
        self.handlers['DrawingInformationEvent'
            ] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'
            ] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'
            ] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'
            ] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    def handle_inner_draw_information_event(self, event):

        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError as e:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(events.DrawingInformationEvent(self.
                    uuid, helpers.get_current_timestamp(), points, color))
        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(
                helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers
                .get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))

    def inner_next_hop_broken_event(self, _):
        ip, port = self.next_next_hop_info
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.
                    sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                self.sending_queue.put(events.TokenReceivedQuestionEvent(
                    self.last_token))
            except Exception as e:
                logger.error(e)
        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    def handle_new_client_request(self, event):
        message_size = event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet = event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))
        first_client = not self.next_hop_info
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in
            range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        next_hop = (helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
            ) if first_client else self.next_hop_info
        response = events.NewClientResponseEvent(next_hop, self.
            next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = len(message).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)
        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                pass
            else:
                logger.error(ex)
                return
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        if self.message_sender:
            self.message_sender.stop()
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.
            sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            self.sending_queue.put(events.TokenPassEvent(self.last_token))

    def handle_drawing_information_event(self, event):

        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)
        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        self.predecessor = event.data['client_address']
        self_address = helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor,
                self_address))
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        else:
            self.sending_queue.put(events.NewNextNextHop(self_address, self
                .predecessor))
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info,
                self_address))
    <mask token>
    <mask token>

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token
        if self.critical_section:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {'timestamp': timestamp, 'client_uuid':
                self.uuid}
            leave_critical_section_deamon = CriticalSectionLeaver(self.
                event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(
                timestamp, self.uuid))
        elif self.sending_queue:
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'
                ] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        elif self.sending_queue:
            self.sending_queue.put(event)

<mask token>


class ModelThread(threading.Thread):

    def __init__(self, event_queue, paint_queue, time_offset, init_data=
        None, init_connection=None):
        super(ModelThread, self).__init__()
        self.event_queue = event_queue
        self.paint_queue = paint_queue
        self.handlers = {}
        self.initialize_handlers()
        self.uuid = uuid.uuid4().hex
        self.want_to_enter_critical_section = False
        self.critical_section = None
        self.time_offset = time_offset
        self.last_token = None
        self.board_state = [[(0) for _ in range(config.getint('Tkinter',
            'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                self.next_next_hop_info = helpers.get_self_ip_address(
                    ), config.getint('NewPredecessorListener', 'Port')
            else:
                self.next_next_hop_info = init_data['next_next_hop']
            self.predecessor = init_connection.getsockname()
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
                self.critical_section = init_data['critical_section_state']
                self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
            init_connection.shutdown(1)
            init_connection.close()
        self.dummy_message_sender = DummyMessageSender(self.event_queue,
            self.uuid)
        self.dummy_message_sender.start()

    def run(self):
        while True:
            e = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in len(board_state):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (
            board_state, 1)})

    def initialize_handlers(self):
        self.handlers['InnerNewClientRequestEvent'
            ] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'
            ] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'
            ] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'
            ] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'
            ] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event
        self.handlers['DrawingInformationEvent'
            ] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'
            ] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'
            ] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'
            ] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    def handle_inner_draw_information_event(self, event):

        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError as e:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(events.DrawingInformationEvent(self.
                    uuid, helpers.get_current_timestamp(), points, color))
        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(
                helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers
                .get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))

    def inner_next_hop_broken_event(self, _):
        ip, port = self.next_next_hop_info
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.
                    sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                self.sending_queue.put(events.TokenReceivedQuestionEvent(
                    self.last_token))
            except Exception as e:
                logger.error(e)
        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    def handle_new_client_request(self, event):
        message_size = event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet = event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))
        first_client = not self.next_hop_info
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in
            range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        next_hop = (helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
            ) if first_client else self.next_hop_info
        response = events.NewClientResponseEvent(next_hop, self.
            next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = len(message).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)
        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                pass
            else:
                logger.error(ex)
                return
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        if self.message_sender:
            self.message_sender.stop()
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.
            sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            self.sending_queue.put(events.TokenPassEvent(self.last_token))

    def handle_drawing_information_event(self, event):

        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)
        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        self.predecessor = event.data['client_address']
        self_address = helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor,
                self_address))
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        else:
            self.sending_queue.put(events.NewNextNextHop(self_address, self
                .predecessor))
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info,
                self_address))
    <mask token>

    def handle_leaving_critical_section(self, event):
        data = event.data
        if data['client_uuid'] == self.uuid:
            return
        if self.critical_section and self.critical_section['client_uuid'
            ] == event.data['client_uuid']:
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            self.critical_section = None
        self.sending_queue.put(event)

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token
        if self.critical_section:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {'timestamp': timestamp, 'client_uuid':
                self.uuid}
            leave_critical_section_deamon = CriticalSectionLeaver(self.
                event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(
                timestamp, self.uuid))
        elif self.sending_queue:
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'
                ] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        elif self.sending_queue:
            self.sending_queue.put(event)

<mask token>


class ModelThread(threading.Thread):

    def __init__(self, event_queue, paint_queue, time_offset, init_data=
        None, init_connection=None):
        super(ModelThread, self).__init__()
        self.event_queue = event_queue
        self.paint_queue = paint_queue
        self.handlers = {}
        self.initialize_handlers()
        self.uuid = uuid.uuid4().hex
        self.want_to_enter_critical_section = False
        self.critical_section = None
        self.time_offset = time_offset
        self.last_token = None
        self.board_state = [[(0) for _ in range(config.getint('Tkinter',
            'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                self.next_next_hop_info = helpers.get_self_ip_address(
                    ), config.getint('NewPredecessorListener', 'Port')
            else:
                self.next_next_hop_info = init_data['next_next_hop']
            self.predecessor = init_connection.getsockname()
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
                self.critical_section = init_data['critical_section_state']
                self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
            init_connection.shutdown(1)
            init_connection.close()
        self.dummy_message_sender = DummyMessageSender(self.event_queue,
            self.uuid)
        self.dummy_message_sender.start()

    def run(self):
        while True:
            e = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in len(board_state):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (
            board_state, 1)})

    def initialize_handlers(self):
        self.handlers['InnerNewClientRequestEvent'
            ] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'
            ] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'
            ] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'
            ] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'
            ] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event
        self.handlers['DrawingInformationEvent'
            ] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'
            ] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'
            ] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'
            ] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    def handle_inner_draw_information_event(self, event):

        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError as e:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(events.DrawingInformationEvent(self.
                    uuid, helpers.get_current_timestamp(), points, color))
        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(
                helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers
                .get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))

    def inner_next_hop_broken_event(self, _):
        ip, port = self.next_next_hop_info
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.
                    sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                self.sending_queue.put(events.TokenReceivedQuestionEvent(
                    self.last_token))
            except Exception as e:
                logger.error(e)
        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    def handle_new_client_request(self, event):
        message_size = event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet = event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))
        first_client = not self.next_hop_info
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in
            range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        next_hop = (helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
            ) if first_client else self.next_hop_info
        response = events.NewClientResponseEvent(next_hop, self.
            next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = len(message).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)
        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                pass
            else:
                logger.error(ex)
                return
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        if self.message_sender:
            self.message_sender.stop()
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.
            sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            self.sending_queue.put(events.TokenPassEvent(self.last_token))

    def handle_drawing_information_event(self, event):

        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)
        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        self.predecessor = event.data['client_address']
        self_address = helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor,
                self_address))
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        else:
            self.sending_queue.put(events.NewNextNextHop(self_address, self
                .predecessor))
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info,
                self_address))

    def handle_entering_critical_section(self, event):
        data = event.data
        if data['client_uuid'] == self.uuid:
            return
        self.critical_section = {'timestamp': data['timestamp'],
            'client_uuid': data['client_uuid']}
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
        self.sending_queue.put(event)

    def handle_leaving_critical_section(self, event):
        data = event.data
        if data['client_uuid'] == self.uuid:
            return
        if self.critical_section and self.critical_section['client_uuid'
            ] == event.data['client_uuid']:
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            self.critical_section = None
        self.sending_queue.put(event)

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token
        if self.critical_section:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {'timestamp': timestamp, 'client_uuid':
                self.uuid}
            leave_critical_section_deamon = CriticalSectionLeaver(self.
                event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(
                timestamp, self.uuid))
        elif self.sending_queue:
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'
                ] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        elif self.sending_queue:
            self.sending_queue.put(event)

import threading
import logging
import uuid
import events
import queue
import helpers
import socket
from config_wrapper import config
from critical_section_leaver import CriticalSectionLeaver
from painter import DrawingQueueEvent
from message_sender import MessageSender
from dummy_message_sender import DummyMessageSender
import json
logger = logging.getLogger(__name__)


class ModelThread(threading.Thread):

    def __init__(self, event_queue, paint_queue, time_offset, init_data=
        None, init_connection=None):
        super(ModelThread, self).__init__()
        self.event_queue = event_queue
        self.paint_queue = paint_queue
        self.handlers = {}
        self.initialize_handlers()
        self.uuid = uuid.uuid4().hex
        self.want_to_enter_critical_section = False
        self.critical_section = None
        self.time_offset = time_offset
        self.last_token = None
        self.board_state = [[(0) for _ in range(config.getint('Tkinter',
            'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                self.next_next_hop_info = helpers.get_self_ip_address(
                    ), config.getint('NewPredecessorListener', 'Port')
            else:
                self.next_next_hop_info = init_data['next_next_hop']
            self.predecessor = init_connection.getsockname()
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
                self.critical_section = init_data['critical_section_state']
                self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
            init_connection.shutdown(1)
            init_connection.close()
        self.dummy_message_sender = DummyMessageSender(self.event_queue,
            self.uuid)
        self.dummy_message_sender.start()

    def run(self):
        while True:
            e = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in len(board_state):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (
            board_state, 1)})

    def initialize_handlers(self):
        self.handlers['InnerNewClientRequestEvent'
            ] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'
            ] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'
            ] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'
            ] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'
            ] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event
        self.handlers['DrawingInformationEvent'
            ] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'
            ] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'
            ] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'
            ] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    def handle_inner_draw_information_event(self, event):

        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError as e:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(events.DrawingInformationEvent(self.
                    uuid, helpers.get_current_timestamp(), points, color))
        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(
                helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers
                .get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))

    def inner_next_hop_broken_event(self, _):
        ip, port = self.next_next_hop_info
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.
                    sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                self.sending_queue.put(events.TokenReceivedQuestionEvent(
                    self.last_token))
            except Exception as e:
                logger.error(e)
        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.
                sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    def handle_new_client_request(self, event):
        message_size = event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet = event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))
        first_client = not self.next_hop_info
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in
            range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        next_hop = (helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
            ) if first_client else self.next_hop_info
        response = events.NewClientResponseEvent(next_hop, self.
            next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = len(message).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)
        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                pass
            else:
                logger.error(ex)
                return
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        if self.message_sender:
            self.message_sender.stop()
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.
            sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            self.sending_queue.put(events.TokenPassEvent(self.last_token))

    def handle_drawing_information_event(self, event):

        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x, y = point
                    self.board_state[x][y] = color
            except IndexError:
                return
            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data':
                (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)
        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        self.predecessor = event.data['client_address']
        self_address = helpers.get_self_ip_address(), config.getint(
            'NewPredecessorListener', 'Port')
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor,
                self_address))
            self.next_next_hop_info = helpers.get_self_ip_address(
                ), config.getint('NewPredecessorListener', 'Port')
        else:
            self.sending_queue.put(events.NewNextNextHop(self_address, self
                .predecessor))
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info,
                self_address))

    def handle_entering_critical_section(self, event):
        data = event.data
        if data['client_uuid'] == self.uuid:
            return
        self.critical_section = {'timestamp': data['timestamp'],
            'client_uuid': data['client_uuid']}
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
        self.sending_queue.put(event)

    def handle_leaving_critical_section(self, event):
        data = event.data
        if data['client_uuid'] == self.uuid:
            return
        if self.critical_section and self.critical_section['client_uuid'
            ] == event.data['client_uuid']:
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            self.critical_section = None
        self.sending_queue.put(event)

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token
        if self.critical_section:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {'timestamp': timestamp, 'client_uuid':
                self.uuid}
            leave_critical_section_deamon = CriticalSectionLeaver(self.
                event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(
                timestamp, self.uuid))
        elif self.sending_queue:
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'
                ] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        elif self.sending_queue:
            self.sending_queue.put(event)

import threading
import logging
import uuid
import events
import queue
import helpers
import socket
from config_wrapper import config
from critical_section_leaver import CriticalSectionLeaver
from painter import DrawingQueueEvent
from message_sender import MessageSender
from dummy_message_sender import DummyMessageSender
import json


logger = logging.getLogger(__name__)

class ModelThread(threading.Thread):
    def __init__(self, event_queue, paint_queue, time_offset, init_data=None, init_connection=None):
        super(ModelThread, self).__init__()

        # Queues
        self.event_queue = event_queue
        self.paint_queue = paint_queue

        #Event handling
        self.handlers = {}
        self.initialize_handlers()

        # Unique uuid identifying clients in the network
        self.uuid = uuid.uuid4().hex
        # Flag indicating weather we want to enter critical section when the token comes
        self.want_to_enter_critical_section = False
        # Information about critical section like the timestamp and client uuid which is in the section
        self.critical_section = None
        # Time offset between ntp server and local time
        self.time_offset = time_offset
        # Value of the last token we have received
        self.last_token = None

        # Initial board state
        self.board_state = [[0 for _ in range(config.getint('Tkinter', 'CanvasY'))] for _ in range(config.getint('Tkinter', 'CanvasX'))]

        # If we are the first client
        if not init_data:
            self.next_hop_info = None
            self.next_next_hop_info = None
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
        else:
            self.next_hop_info = init_data['next_hop']
            if not init_data['next_next_hop']:
                # If there is no next_next_hop init data in the response we are the second client so we set
                # next next hop as our address
                self.next_next_hop_info = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
            else:
                # If there are more thant two clients we set the value from the response
                self.next_next_hop_info = init_data['next_next_hop']

            # Address of our predecessor
            self.predecessor = init_connection.getsockname()

            # We initialize connection to our next hop and we start sending queue
            ip, port = init_data['next_hop']
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
            self.message_sender.start()
            self.initialize_board(init_data['board_state'])
            if init_data['critical_section_state']:
              self.critical_section = init_data['critical_section_state']
              self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})



            # We signal that client has initialized properly
            init_connection.shutdown(1)
            init_connection.close()

        # We start a dummy message sender event which will create dummy messages to detect connection breaks
        self.dummy_message_sender = DummyMessageSender(self.event_queue, self.uuid)
        self.dummy_message_sender.start()


    def run(self):
        while True:
            (e) = self.event_queue.get()
            handler_function = self.handlers[type(e).__name__]
            handler_function(e)

    def initialize_board(self, board_state):
        for counter in (len(board_state)):
            x_coord, y_coord = board_state[counter]
            try:
                self.board_state[x_coord][y_coord] = 1
            except IndexError:
                return
        self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (board_state, 1)})


    def initialize_handlers(self):
        # Inner Handlers
        self.handlers['InnerNewClientRequestEvent'] = self.handle_new_client_request
        self.handlers['InnerNewPredecessorRequestEvent'] = self.handle_new_predecessor_request_event
        self.handlers['InnerDrawingInformationEvent'] = self.handle_inner_draw_information_event
        self.handlers['InnerWantToEnterCriticalSection'] = self.inner_handle_want_to_enter_critical_section_event
        self.handlers['InnerLeavingCriticalSection'] = self.inner_leaving_critical_section_event
        self.handlers['InnerNextHopBroken'] = self.inner_next_hop_broken_event

        # Outter handlers
        self.handlers['DrawingInformationEvent'] = self.handle_drawing_information_event
        self.handlers['EnteredCriticalSectionEvent'] = self.handle_entering_critical_section
        self.handlers['LeavingCriticalSectionEvent'] = self.handle_leaving_critical_section
        self.handlers['TokenPassEvent'] = self.handle_token_pass_event
        self.handlers['NewNextNextHop'] = self.handle_new_next_next_hop_event
        self.handlers['TokenReceivedQuestionEvent'] = self.handle_token_received_question_event
        self.handlers['DummyMessageEvent'] = self.handle_dummy_message_event

    ############################################################################################
    #
    #                                      Inner Event handlers
    ############################################################################################
    def handle_inner_draw_information_event(self, event):
        def draw_points(event):
            color = event.data['color']
            points = event.data['points']
            try:
                for point in points:
                  x,y = point
                  self.board_state[x][y] = color
            except IndexError as e:
                return

            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (points, color)})
            if (self.sending_queue):
                self.sending_queue.put(
                    events.DrawingInformationEvent(self.uuid, helpers.get_current_timestamp(), points, color))

        if not self.critical_section:
            draw_points(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_points(event)
        elif self.critical_section['client_uuid'] == self.uuid:
            draw_points(event)
        elif self.critical_section['client_uuid'] != self.uuid:
            pass

    def inner_handle_want_to_enter_critical_section_event(self, _):
        self.want_to_enter_critical_section = True

    def inner_leaving_critical_section_event(self, _):
        self.critical_section = None
        self.paint_queue.put({"type": DrawingQueueEvent.BOARD_OPEN})
        if self.sending_queue:
            self.sending_queue.put(events.LeavingCriticalSectionEvent(helpers.get_current_timestamp(), self.uuid))
            self.sending_queue.put(events.TokenPassEvent(self.last_token))
        else:
            self.event_queue.put(events.LeavingCriticalSectionEvent(helpers.get_current_timestamp(), self.uuid))
            self.event_queue.put(events.TokenPassEvent(self.last_token))


    def inner_next_hop_broken_event(self, _):
        # If we detect that the next hop connection is down we want to:
        # 1.Try to reconnect to the client
        # 2.If reconnect fails we want to connect to our next next hop
        # 3.When we succesfully connect to our next next hop we want to send recovery token question
        #   in case that the dead client was holding the token the moment he died
        ip, port = self.next_next_hop_info
        # If we are the only client left we reset the data to the initial state
        if ip == helpers.get_self_ip_address():
            self.critical_section = None
            self.next_hop_info = None
            self.next_next_hop_info = None
            if self.message_sender:
                self.message_sender.stop()
            self.sending_queue = None
            self.message_sender = None
            self.predecessor = None
            self.last_token = 0
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            return

        def connect_to_next_next_hop(self):
            ip, port = self.next_next_hop_info
            try:
                s = socket.create_connection((ip, port))
                self.sending_queue = queue.Queue(maxsize=0)
                self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
                self.message_sender.start()
                self.next_hop_info = self.next_next_hop_info
                # After we connect to a new client we have to check whether the dead client wasn't in posession
                # of token
                self.sending_queue.put(events.TokenReceivedQuestionEvent(self.last_token))
            except Exception as e:
                logger.error(e)

        ip, port = self.next_hop_info
        try:
            s = socket.create_connection((ip, port))
            self.sending_queue = queue.Queue(maxsize=0)
            self.message_sender = MessageSender(self.event_queue, self.sending_queue, s)
            self.message_sender.start()
        except ConnectionRefusedError as e:
            logger.error(e)
            connect_to_next_next_hop(self)

    ############################################################################################
    #
    #                                      Event handlers
    ############################################################################################
    def handle_new_client_request(self, event):
        # At first we want to receive information to properly connect as new predecessor after sending init_data
        message_size =  event.data['connection'].recv(8)
        message_size = int.from_bytes(message_size, byteorder='big')
        message = b''
        while len(message) < message_size:
            packet =  event.data['connection'].recv(message_size - len(message))
            if not packet:
                return None
            message += packet
        client_request = json.loads(message.decode('utf-8'))

        first_client = not self.next_hop_info
        # When we detect a new client connecting we want to;
        # 1.Send him the initial data over the connection we already established
        # 2.Connect to him as a predecessor

        # Gather the initial board state (only the coloured spots)
        marked_spots = [(x, y) for x in range(len(self.board_state)) for y in range(len(self.board_state[x])) if self.board_state[x][y] == 1]
        # If we have next hop information we send it, if we do not have we are the first client so we send our
        # information as the first hop information
        next_hop = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port')) if first_client else self.next_hop_info
        # If we are the first client next next hop is None
        response = events.NewClientResponseEvent(next_hop, self.next_next_hop_info, marked_spots, self.critical_section)
        message = helpers.event_to_message(response)
        message_size = (len(message)).to_bytes(8, byteorder='big')
        event.data['connection'].send(message_size)
        event.data['connection'].send(message)


        try:
            message = event.data['connection'].recv(8)
        except Exception as ex:
            if message == b'':
                # Only case when we have a succesfull read of 0 bytes is when other socket shutdowns normally
                pass
            else:
                logger.error(ex)
                #Client did not initializ correctly so we abort the process
                return
        # If we are not the first client we have to update our next next hop to our previous next hop
        if not first_client:
            self.next_next_hop_info = self.next_hop_info
        else:
            # If we are the first client we update our next next hop info to self address
            self.next_next_hop_info = (
                helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port')
            )

        # We stop current message sender if it exists
        if self.message_sender:
            self.message_sender.stop()


        # We update our next hop info with the newest client request
        self.next_hop_info = client_request['data']['address']
        ip, port = self.next_hop_info
        # We establish a new connection and a new message sender
        connection = socket.create_connection((ip, port), 100)
        self.sending_queue = queue.Queue(maxsize=0)
        self.message_sender = MessageSender(self.event_queue, self.sending_queue, connection)
        self.message_sender.start()
        if first_client and self.last_token != None:
            # If we are the first client we start passing of the token
            self.sending_queue.put(events.TokenPassEvent(self.last_token))


    def handle_drawing_information_event(self, event):
        def draw_point(event):
            points = event.data['points']
            color = event.data['color']
            try:
                for point in points:
                    x,y = point
                    self.board_state[x][y] = color
            except IndexError:
                return

            self.paint_queue.put({'type': DrawingQueueEvent.DRAWING, 'data': (points, color)})
            if self.sending_queue:
                self.sending_queue.put(event)

        if event.data['client_uuid'] == self.uuid:
            return
        if not self.critical_section:
            draw_point(event)
        elif self.critical_section['timestamp'] > event.data['timestamp']:
            draw_point(event)
        elif self.critical_section['client_uuid'] == event.data['client_uuid']:
            draw_point(event)
        elif self.critical_section['client_uuid'] != event.data['client_uuid']:
            pass

    def handle_new_predecessor_request_event(self, event):
        # The moment we have a new predecessor this means that the client before our predecessor
        # has a new next next hop address (which is our address) and our predecessor has new next next hop (which is
        # our next hop)
        self.predecessor = event.data['client_address']
        self_address = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
        # Special case if we have only 2 nodes left
        if self.predecessor[0] == self.next_hop_info[0]:
            self.sending_queue.put(events.NewNextNextHop(self.predecessor, self_address))
            self.next_next_hop_info = (helpers.get_self_ip_address(), config.getint('NewPredecessorListener', 'Port'))
        else:
            # We send information to predecessor of our predecessor about his new next next hop address
            self.sending_queue.put(events.NewNextNextHop(self_address, self.predecessor))
            # We send information to our predecessor about his new next next hop
            self.sending_queue.put(events.NewNextNextHop(self.next_hop_info, self_address))

    def handle_entering_critical_section(self, event):
        data = event.data
        if (data['client_uuid']) == self.uuid:
            return
        self.critical_section = {
            'timestamp': data['timestamp'],
            'client_uuid': data['client_uuid']
        }
        self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CLOSED})
        self.sending_queue.put(event)

    def handle_leaving_critical_section(self, event):
        data = event.data
        if (data['client_uuid']) == self.uuid:
            return

        if self.critical_section and self.critical_section['client_uuid'] == event.data['client_uuid']:
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            self.critical_section = None

        self.sending_queue.put(event)

    def handle_token_pass_event(self, event):
        token = event.data['token'] + 1
        self.last_token = token

        if self.critical_section:
            # If we have received the token and the critical section exists we unvalidate critical secion info
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})

        if self.want_to_enter_critical_section:
            timestamp = helpers.get_current_timestamp()
            self.critical_section = {
                'timestamp': timestamp,
                'client_uuid': self.uuid
            }
            leave_critical_section_deamon = CriticalSectionLeaver(self.event_queue)
            leave_critical_section_deamon.start()
            self.want_to_enter_critical_section = False
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_CONTROLLED})
            self.sending_queue.put(events.EnteredCriticalSectionEvent(timestamp, self.uuid))
        else:
            if self.sending_queue:
                self.sending_queue.put(events.TokenPassEvent(token))

    def handle_new_next_next_hop_event(self, event):
        post_destination_ip, _ = event.data['destination_next_hop']
        next_hop_ip, _ = self.next_hop_info

        # We are the recipient of the message
        if post_destination_ip == next_hop_ip:
            self.next_next_hop_info = event.data['new_address']
        else:
            self.sending_queue.put(event)

    def handle_token_received_question_event(self, event):
        # We check weather the last token we received is greater than
        # the token from the request
        # If it is, this means that the disconnected client was not in posession of the token when he disconnected
        # If it was we have to unvalidate critial secion information and send token further

        if self.last_token > event.data['token'] + 1:
            return
        else:
            self.critical_section = None
            self.paint_queue.put({'type': DrawingQueueEvent.BOARD_OPEN})
            token = event.data['token'] + 1 if event.data['token'] else self.last_token + 1
            self.sending_queue.put(events.TokenPassEvent(token))

    def handle_dummy_message_event(self, event):
        if self.uuid != event.data['uuid']:
            return
        else:
            if self.sending_queue:
                self.sending_queue.put(event)
