import json
import socket
import threading

from Classes.System.Network.EventHandler import UDPEventHandler, TCPEventHandler

class NetworkManager:

    class UDP:
        __Addr:str
        __Port:str
        __BufferSize:int = 1024
        __udpSocket:socket
        __users:list
        __eventHandler: list
        __UDPClientThread:threading.Thread
        
        def __init__(self, Addr:str, Port:str):
            self.__Addr = Addr
            self.__Port = int(Port)

            self.__users = {}
            self.__eventHandler = {}
            self.__udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.__udpSocket.bind((self.__Addr, self.__Port))
                                    
            self.__UDPClientThread = threading.Thread(target=self.accept_connections)
            self.__UDPClientThread.start()

        def accept_connections(self):
            while True:
                client_udp_socket, client_address = self.__udpSocket.accept()
                print(f"Connected with {client_address}")
                
                self.__users[client_address] = client_udp_socket
                self.__eventHandler[client_address] = TCPEventHandler(client_udp_socket)
                
                client_handler_thread = threading.Thread(target=self.receive, args=(client_udp_socket, client_address))
                client_handler_thread.start()

        # handles incomming udp data
        def receive(self):
            while True:
                data, address = self.__udpSocket.recvfrom(self.__BufferSize)
                message = data.decode()
                messageJson = json.loads(message)
                user = messageJson.get("user")

                if user not in self.__users:
                    self.__users[user] = address  # Storing new user in dictionary

                # Process the message and handle accordingly
                self.__eventHandler.handleUDPEvents(messageJson)
                print(f"Received message from {address}: {message}")

        def broadcast(self, payload:dict):
            for user_address in self.__users.values():
                self.__udpSocket.sendto(json.dumps(payload).encode(), user_address)

        def send(self, payload:dict, user:str):
            if user in self.__users:
                self.__udpSocket.sendto(json.dumps(payload).encode(), self.__users[user])
                
    class TCP:
        __Addr:str
        __Port:str
        __BufferSize:int = 1024
        __tcpSocket:socket
        __users:list
        __eventHandler: list        
        __TCPClientThread:threading.Thread
        
        def __init__(self, Addr:str, Port:str):
            self.__Addr = Addr
            self.__Port = int(Port)

            self.__users = {}
            self.__eventHandler = {}
            self.__tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.__tcpSocket.bind((self.__Addr, self.__Port))
                                    
            self.__TCPClientThread = threading.Thread(target=self.accept_connections)
            self.__TCPClientThread.start()

        def accept_connections(self):
            while True:
                client_tcp_socket, client_address = self.__tcpSocket.accept()
                print(f"Connected with {client_address}")
                self.__users[client_address] = client_tcp_socket
                self.__eventHandler[client_address] = TCPEventHandler(client_tcp_socket)
                client_handler_thread = threading.Thread(target=self.receive, args=(client_tcp_socket, client_address))
                client_handler_thread.start()

        # handles incomming udp data
        def receive(self):
            while True:
                data, address = self.__tcpSocket.recvfrom(self.__BufferSize)
                message = data.decode()
                messageJson = json.loads(message)
                user = messageJson.get("user")

                if user not in self.__users:
                    self.__users[user] = address  # Storing new user in dictionary

                # Process the message and handle accordingly
                self.__eventHandler.handleTCPEvents(messageJson)
                print(f"Received message from {address}: {message}")

        def broadcast(self, payload:dict):
            for user_address in self.__users.values():
                self.__tcpSocket.sendto(json.dumps(payload).encode(), user_address)

        def send(self, payload:dict, user:str):
            if user in self.__users:
                self.__tcpSocket.sendto(json.dumps(payload).encode(), self.__users[user])
            
    tcp: TCP
    udp: UDP

    def __init__(self, Addr:str, TCPPort:str, UDPPort:str):
        self.tcp = self.TCP(Addr, TCPPort)
        self.udp = self.UDP(Addr, UDPPort)
