from ..client.game_client import GameClient
import asyncio
from loguru import logger
from typing import Any



class Map(GameClient):
    
    
    async def get_map_chunk(self, kingdom, x, y):
        
        try:
            
            await self.send_json_message("gaa",{
                "KID": kingdom,
                "AX1": x,
                "AY1": y,
                "AX2": x + 12,
                "AY2": y + 12
            })
            
            
            
        except Exception as e:
            logger.error(e)
            return False
    
    
    async def get_closest_npc(
        self,
        kingdom: int,
        npc_type: int,
        min_level: int = 1,
        max_level: int = -1,
        owner_id: int = -1,
        sync: bool = True
        ) -> dict | bool:
        
        try:
            await self.send_json_message(
                "fnm",
                {
                    
                    "T": npc_type,
                    "KID": kingdom,
                    "LMIN": min_level,
                    "LMAX": max_level,
                    "NID": owner_id
                }
            )
            if sync:
                response = await self.wait_for_response("fnm")
                return response
            return True
            
        except Exception as e:
            logger.error(e)
            return False
        
        

    
    def _spiral_coords(self, max_radius: int, cx: int, cy: int):
        step = 13
        seen = set()
        for radius in range(step, max_radius + 1, step):

            for x in range(cx - radius, cx + radius + 1, step):
                pt = (x, cy - radius)
                if pt not in seen:
                    seen.add(pt)
                    yield pt
           
            for y in range(cy - radius, cy + radius + 1, step):
                pt = (cx + radius, y)
                if pt not in seen:
                    seen.add(pt)
                    yield pt
            
            for x in range(cx + radius, cx - radius - 1, -step):
                pt = (x, cy + radius)
                if pt not in seen:
                    seen.add(pt)
                    yield pt
           
            for y in range(cy + radius, cy - radius - 1, -step):
                pt = (cx - radius, y)
                if pt not in seen:
                    seen.add(pt)
                    yield pt
     
    
    
    
    async def map_scanner(
        self,
        kingdom: int,
        max_radius: int,
        castle_x: int,
        castle_y: int,
        handler: Any
        ):

        
        processing_task = asyncio.create_task(
            self.process_data("gaa", handler, interval=0.5)
        )
        try:
            logger.info("Scanning...")
            
            for x, y in self._spiral_coords(max_radius=max_radius, cx=castle_x, cy=castle_y):
                await self.get_map_chunk(kingdom, x, y)
                await asyncio.sleep(0.01)
            logger.info("Complete")
        finally:
            
            processing_task.cancel()
            
            
     
     
            
    async def find_by_name(
        self,
        user_name: str,
        sync: bool = True
    ) -> dict | bool:
        
        try:
            await self.send_json_message("wsp", {"PN": user_name})
            if sync:
                response = await self.wait_for_response("wsp")
                return response
            return True
        
        except Exception as e:
            logger.error(e)
            return False
        
    
    
    async def get_npc_target_infos(
        self,
        tx: int,
        ty: int,
        sx: int,
        sy: int,
        kid: int,
        sync: bool = True
    ) -> dict | bool:
        
        try:
            await self.send_json_message(
                "adi",
                {
                    "TX": tx,
                    "TY": ty,
                    "SX": sx,
                    "SY": sy,
                    "KID": kid
                }
            )
            if sync:
                response = await self.wait_for_response("adi")
                return response
            return True
        
        except Exception as e:
            logger.error(e)
            return False
        




    async def get_pvp_target_infos(
        self,
        tx: int,
        ty: int,
        sx: int,
        sy: int,
        kid: int,
        sync: bool = True
    ) -> dict | bool:
        
        try:
            await self.send_json_message(
                "aci",
                {
                    "TX": tx,
                    "TY": ty,
                    "SX": sx,
                    "SY": sy,
                    "KID": kid
                }
            )
            if sync:
                response = await self.wait_for_response("aci")
                return response
            return True
        
        except Exception as e:
            logger.error(e)
            return False
        
        
        
        
    def periodic_map_scanner(
        self,
        kingdom: int,
        max_radius: int,
        castle_x: int,
        castle_y: int,
        handler: Any,
        interval: float,
        run_immediately: bool = True
    ):
        loop = asyncio.get_running_loop()
        
        async def _scan_and_reschedule():
            
            try:
                await self.map_scanner(kingdom, max_radius, castle_x, castle_y, handler)
            
            except Exception as e:
                logger.error(e)
            
            finally:
                loop.call_later(interval, lambda: loop.create_task(_scan_and_reschedule()))
                
            if run_immediately:
                loop.create_task(_scan_and_reschedule())
            
            else:
                loop.call_later(interval, lambda: loop.create_task(_scan_and_reschedule()))