from .packet import Packet
from .EnumQuery import EnumQuery
from .EnumResponse import EnumResponse
from .CFrame import CFrame
from .DFrame import DFrame
import traceback
import uuid
from utils.logger import setup_logger
from giants import GameTypes, Teams
import asyncio

logger = setup_logger(__name__)


class Netserver(asyncio.DatagramProtocol):
    def __init__(self, server):
        self.remotesocket = None
        self.statssocket = None
        self.server = server
        self.addrs = []
        self.guid = uuid.uuid4().bytes
        self.incoming_packets = []
        super().__init__()

    def connection_made(self, transport):
        logger.debug("Connection made")
        self.remotesocket = transport

    def datagram_received(self, data, addr):
        loop = asyncio.get_event_loop()
        loop.create_task(self.handle_new_packet(addr, data))

    async def handle_new_packet(self, addr, bData):
        pPacket = Packet(bData)
        first = pPacket.getByte()
        if len(bData) >= 4 and first & DFrame.PACKET_COMMAND_DATA:
            return
        elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME:
            return
        elif first == EnumQuery.LEAD or first == EnumResponse.LEAD:
            logger.debug("%s:%s > %s (ENUM)", addr[0], addr[1], bData.hex())
            await self.handle_new_enum(addr, pPacket)
        else:
            logger.error("Unknown frame received")
            return

    async def handle_new_enum(self, addr, data):
        # EnumQuery or EnumResponse
        second = data.getByte()
        if second == EnumQuery.COMMAND:
            # EnumQuery
            try:
                eq = EnumQuery(data)
                version, server_name, player_count, max_player_count, map_name = await self.server.on_connection_received(addr[0], addr[1])
                er = EnumResponse()
                er.Payload = eq.Payload
                er.ApplicationDescSize = 0x50
                er.ApplicationDescFlags = 0x41
                er.MaxPlayers = max_player_count
                er.CurrentPlayers = player_count
                er.SessionName = server_name
                er.ApplicationInstanceGUID = self.guid
                er.ApplicationGUID = eq.ApplicationGUID

                appdata = Packet()
                appdata.putByte(0xff)  # map ID
                appdata.putByte(GameTypes.GTypeNull)  # game type
                appdata.putByte(Teams.MvRvK)  # teams
                appdata.write(b'\x00')  # original: 0x00 - does not seem to affect client
                appdata.putShort(int(version * 1000))
                appdata.write(b'\x02\x92')  # original: 0292 - does not seem to affect client
                appdata.putShort(0)  # points per capture
                appdata.putShort(0)  # points per kill
                appdata.write(b'\x00\x00')  # original: 0000 - does not seem to affect client
                appdata.putShort(0)  # detente time
                appdata.write(b"\xff\xff\xff\xff")  # Seems to be a checksum of current map OR linked to the number of chars in the map name
                appdata.write(map_name.encode("ascii"))
                appdata.write(b'\x00' * (32 - len(map_name)))
                er.ApplicationReservedData = appdata.getvalue()

                er.ApplicationReservedData += b'\x00' * (32 - len(map_name))
                self.send_packet(addr, er.to_packet())
            except Exception:
                logger.error("Could not parse EnumQuery or forge EnumResponse: ")
                traceback.print_exc()
                return
        else:
            logger.error("Unknown DPLHP command: %s", second)

    def send_packet(self, addr, packet):
        self.remotesocket.sendto(packet.getvalue(), addr)