from apps.apps_list import AppsList
from core.network_message import NetworkMessage

print_debug = True

class AppClientConnection:
    '''
        Class passed to server apps instead of "direct" client connections
        in order to perform operations (i.e. consensus) before replying to clients
    '''

    # first version, simply forward everything to the direct client connection

    def __init__(self, client):
        self.client_ref = client
        self.username = client.username
    
    def reinit_username(self):
        self.username = self.client_ref.username

    # send a NetworkMessage to the physical client of the connection 
    def send_message(self, message):
        if print_debug:
            print("Sending message to client of:", message.network_path, message.command)
        self.client_ref.send_message(message)

    # convenience function : send a message with path, command and content arguments instead if NetworkMessage
    def send(self, network_path, command, content):
        message = NetworkMessage(network_path, command, content)
        self.send_message(message)

    # return the delay in seconds since the last message
    def last_message_delay(self):
        return self.client_ref.last_message_delay()
    
    # note : for now, don't close conections
    def lose_connection(self):
        pass
        #self.client_ref.lose_connection()


class ServerServices():
    '''
        Class to connect client connections to server app services
        Singleton class (all member functions are static)
    '''

    services_dict = {}
    client_dict= {}
    available_id = 0

    def connection_made(client):
        # currently, we do nothing special when a connection is established
        # we wait the first message in order to know what is the target network_path and associated service
        app_client = AppClientConnection(client)
        client.connection_id = ServerServices.available_id
        ServerServices.client_dict[client.connection_id] = (client, app_client)
        ServerServices.available_id += 1

    # receive a NetworkMessage from a client
    def receive_message(client, message):
        if print_debug:
            print("received message for address ", message.network_path, message.command)
        
        if client.connection_id is None:
            print("client connection not correctly initialized")
            return

        # find the correct app client object to be passed to the app
        app_client = ServerServices.client_dict[client.connection_id][1]
        
        #currently, need to reinit username (user may have logged since last connection use)
        app_client.reinit_username()

        # choose the service depending on the target network address
        app_key = AppsList.get_server_app_key(message.network_path)
        if app_key is None:
            # trying to access to a service not defined yet
            # MEMO : would be better to reply something instead of closing the connection ?
            client.lose_connection()
            return
        else:
            # create the correct server app if not existing yet
            # note : python dictionary.setdefault always creates an object (potentially unused), so better not use here 
            if app_key not in ServerServices.services_dict:
                ServerServices.services_dict[app_key] = AppsList.get_server_app(message.network_path)
            
            # forward the message to the server app receiver function
            ServerServices.services_dict[app_key].receive_message(app_client, message)

    def connection_lost(client):
        if client.connection_id is not None:
            ServerServices.client_dict.pop(client.connection_id, None)
