from typing import List, Tuple
import random as rnd
import pygame.draw
import pygame
from coopstructs.geometry.vectors.vectorN import Vector2
from coopgame.gameTemplate import GameTemplate, BuiltInSurfaceType
from coopgame.chunkRendering import ChunkRenderer, chunk_of_tile
from coopgame.tilemap import Tilemap, FileTileset
from cooptools.colors import Color
import coopgame.pygamehelpers as help
from coopstructs.geometry import Rectangle
from cooptools.toggles import BooleanToggleable
from coopgame.spriteHandling.sprites import AnimatedSprite, SpriteOriginPosition
from coopgame.spriteHandling.spritesheet import SpriteSheet
from coopgame.handleKeyPressedArgs import InputState, InputStateHandler, InputAction, InputEvent, InputEventType, basic_key_actions, CallbackPackage
from coopgame.pygame_k_constant_names import PyKeys, PyMouse
from coopgame.scrollHandler import ScrollTileHandler
from cooptools.sectors import rect_utils as sec_util

file = '../coopgame/assets/tileSets/tmw_desert_spacing.png'

DELTA_AMT_PER_100MS = 20
WIDTH_DIM_P = (0.25, 0.65)
HEIGHT_DIM_P = (0.15, 0.85)
BG_TILE_STATE_HEADER = 'tileType'
TILE_HAS_PRIZE_HEADER = "hasPrize"

def dummy_tile_type_resolver(target: Tuple[int, int]) -> int:
    # thresh = 15
    # if target[1] > thresh:
    #     return 7 # underground
    # if target[1] == thresh:
    #     return 6 # surface
    # if target[1] == thresh - 1:
    #     if rnd.randint(1, 5) == 1:
    #         return rnd.choice([34, 39, 40, 41, 46, 47]) # foliage
    if target == (0, 0):
        return 35  # flag
    if rnd.randint(1, 20) == 1:
        return rnd.choice([34, 39, 40, 41, 46, 47])  # foliage
    return 33  # desert

def dummy_has_goal_resolver(target: Tuple[int, int]) -> int:
    if rnd.randint(1, 100) == 1:
        return 1  # has goal
    return 0 # no goal

class ChunkGame(GameTemplate):

    def __init__(self):
        input_state_handler = InputStateHandler(
            quit_callback_package=CallbackPackage(down=lambda x: self.quit()),
            debug_callback_package=CallbackPackage(down=lambda x: self.debug_mode_toggle.visibility_toggle()),
            fullscreen_callback_package=CallbackPackage(down=lambda x: self.toggle_fullscreen()),
            key_actions=[
                InputAction([InputEvent(PyKeys.K_LEFT, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_left(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_a, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_left(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_RIGHT, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_right(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_d, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_right(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_UP, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_up(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_w, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_up(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_DOWN, event_type=InputEventType.HELD)], callback=lambda key_pressed_args: self.head_down(key_pressed_args.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_s, event_type=InputEventType.HELD)], callback=lambda x: self.head_down(x.DeltaMs)),
                InputAction([InputEvent(PyKeys.K_o, event_type=InputEventType.DOWN)], callback=lambda x: self.minimap_show_chunk_outline_toggle.toggle()),
                InputAction([InputEvent(PyKeys.K_p, event_type=InputEventType.DOWN)], callback=lambda x: self.minimap_show_player_chunk_toggle.toggle()),
                InputAction([InputEvent(PyKeys.K_z, event_type=InputEventType.DOWN)], callback=lambda x: input()),
                InputAction([InputEvent(PyKeys.K_t, event_type=InputEventType.DOWN)], callback=lambda x: self.minimap_show_player_tile_toggle.toggle()),
        ])

        super().__init__(screen_height=640, screen_width=960, input_state_handler=input_state_handler)
        self.tileset = FileTileset(file)
        self.tilemap = None

        pygame.display.set_caption("Pygame Tiled Demo")
        self.image = None

        self.chunk_renderer = ChunkRenderer(chunk_size=(16, 16), tile_type_resolvers={BG_TILE_STATE_HEADER: dummy_tile_type_resolver,
                                                                                      TILE_HAS_PRIZE_HEADER: dummy_has_goal_resolver})
        self.scroll = (0, 0)

        self.minimap_surface = pygame.Surface(size=(self.screen_width / 4,self.screen_height / 3))
        self.minimap_show_player_chunk_toggle = BooleanToggleable(default=False)
        self.minimap_show_player_tile_toggle = BooleanToggleable(default=False)
        self.minimap_show_chunk_outline_toggle = BooleanToggleable(default=False)

        character_file = '../coopgame/assets/spriteSheets/DA_male2.png'
        self.character_spritesheet = SpriteSheet(character_file, n_rows=21, n_columns=13, x_margin_left=15, x_margin_right=14, x_padding=33, y_margin_top=14, y_padding=16, y_margin_bottom=1)



        self.char_pos = Vector2(0, 0)
        self.main_character = AnimatedSprite("main"
                                             , Vector2(0, 0)
                                             , {"walkback": tuple(self.character_spritesheet.load_row_strip(8, colorkey=-1)[1:9]),
                                                "walkleft": tuple(self.character_spritesheet.load_row_strip(9, colorkey=-1)[1:9]),
                                                "walkdown": tuple(self.character_spritesheet.load_row_strip(10, colorkey=-1)[1:9]),
                                                "walkright": tuple(self.character_spritesheet.load_row_strip(11, colorkey=-1)[1:9]),
                                                "standright": tuple(self.character_spritesheet.load_row_strip(11, colorkey=-1)[0:0]),
                                                "standleft": tuple(self.character_spritesheet.load_row_strip(9, colorkey=-1)[0:0]),
                                                "standback": tuple(self.character_spritesheet.load_row_strip(8, colorkey=-1)[0:0]),
                                                "standdown": tuple(self.character_spritesheet.load_row_strip(10, colorkey=-1)[0:0]),
                                                "fall": tuple(self.character_spritesheet.load_row_strip(20, colorkey=-1)[:6])}
                                             , default_animation_key="walkdown",
                                             loop=True)

        self.scroll_handler = ScrollTileHandler(
            tile_size=self.tileset.size,
            screen_dims=self.screen.get_size(),
            focal_point=self.char_pos.as_tuple(),
            width_dim_p=WIDTH_DIM_P,
            height_dim_p=HEIGHT_DIM_P
        )
        print(self.chunk_renderer.tile_state((100, 100)))

    def head_left(self, delta_ms):
        self.head_direction(delta_ms, -DELTA_AMT_PER_100MS, 0)
        self.main_character.set_animation('walkleft')

    def head_right(self, delta_ms):
        self.head_direction(delta_ms, DELTA_AMT_PER_100MS, 0)
        self.main_character.set_animation('walkright')

    def head_up(self, delta_ms):
        self.head_direction(delta_ms, 0, -DELTA_AMT_PER_100MS)
        self.main_character.set_animation('walkback')

    def head_down(self, delta_ms):
        self.head_direction(delta_ms, 0, DELTA_AMT_PER_100MS)
        self.main_character.set_animation('walkdown')

    def head_direction(self, delta_ms, delta_x, delta_y):
        self.update_char_pos(delta_ms, delta_x, delta_y)
        _ = self.scroll_handler.update(focal_point=self.char_pos.as_tuple())

    def update_char_pos(self, delta_ms, delta_x, delta_y):
        og_pos = self.char_pos
        self.char_pos += Vector2(delta_x, delta_y) * round(delta_ms / 100, 1)
        char_tile = sec_util.sector_from_coord(
            coord=self.char_pos.as_tuple(),
            sec_dims=self.tileset.size)

        if self.chunk_renderer.tile_state(char_tile)[BG_TILE_STATE_HEADER] != 33:
            self.char_pos = og_pos

    def draw_tilemap(self):
        if self.tilemap and self.tilemap.rect:
            rect = pygame.Rect(self.tilemap.rect[0] - self.scroll_handler.WorldCoordOffsetScrollTileCoord[0],
                               self.tilemap.rect[1] - self.scroll_handler.WorldCoordOffsetScrollTileCoord[1],
                               self.tilemap.rect[2],
                               self.tilemap.rect[3])
            self.screen.blit(self.tilemap.image, rect)

    def draw_hud_text(self):
        help.draw_text(f"Scroll: {str((round(self.scroll_handler.Scroll[0], 2), round(self.scroll_handler.Scroll[1], 2)))}", self.screen)
        help.draw_text(f"WorldTile: {str(self.scroll_handler.WorldTile)}", self.screen, offset_rect=Rectangle.from_tuple((0, self.screen.get_height() - 25, 20, self.screen_width)))
        help.draw_text(f"PosCurrTile: {str(self.scroll_handler.PosInCurrentTile)}", self.screen,
                       offset_rect=Rectangle.from_tuple((0, self.screen.get_height() - 75, 20, self.screen_width)))
        help.draw_text(f"WorldPosOffsetScroll: {str(self.scroll_handler.WorldCoordOffsetScrollTileCoord)}", self.screen,
                       offset_rect=Rectangle.from_tuple((0, self.screen.get_height() - 100, 20, self.screen_width)))

    def draw_minimap(self):
        self.screen.blit(self.minimap_surface,
                         (self.screen.get_width() - self.minimap_surface.get_width(),
                          self.screen.get_height() - self.minimap_surface.get_height()))

    def draw_prizes(self):
        chunk_size = self.chunk_renderer.chunk_size
        pxl_size = self.tileset.size
        chunk_pxls = (chunk_size[0] * pxl_size[0], chunk_size[1] * pxl_size[1])

        prizes = self.chunk_renderer.visible_tile_idx_by_condition(
            scroll=self.scroll_handler.Scroll,
            window_size=self.screen.get_size(),
            tile_evaluator=lambda dic: dic.get(TILE_HAS_PRIZE_HEADER, False) == True,
            chunk_pxls=chunk_pxls,
            buffer_tiles=(1, 1))

        for idx, state in prizes.items():
            window_coord = self.scroll_handler.tile_window_coord(idx)
            help.draw_circle(surface=self.screen, center=Vector2.from_tuple(window_coord), radius=10, color=Color.YELLOW, width=5)

    def draw_character(self):
        self.main_character.set_pos(Vector2.from_tuple(self.scroll_handler.WindowCoords), SpriteOriginPosition.BOTTOM_MIDDLE)
        self.main_character.blit(self.screen,
                                 display_handle=lambda: self.debug_mode_toggle.value,
                                 display_rect=lambda: self.debug_mode_toggle.value)

    def draw(self, frames: int, debug_mode: bool = False):
        self.draw_tilemap()
        self.draw_prizes()
        # self.draw_hud_text()
        self.draw_minimap()
        self.draw_character()


    def update_tilemap(self):
        chunk_size = self.chunk_renderer.chunk_size
        pxl_size = self.tileset.size
        chunk_pxls = (chunk_size[0] * pxl_size[0], chunk_size[1] * pxl_size[1])

        tm_array = self.chunk_renderer.renderable_tile_type_array(scroll=self.scroll_handler.Scroll,
                                                                  window_size=self.screen.get_size(),
                                                                  key=BG_TILE_STATE_HEADER,
                                                                  chunk_pxls=chunk_pxls,
                                                                  buffer_tiles=(1, 1))

        self.tilemap = Tilemap(self.tileset,
                               map=tm_array,
                               surface_size=self.screen.get_size(),
                               buffer_tiles=(1, 1))



    def model_updater(self, delta_time_ms: int):
        # self.head_left(delta_time_ms)
        self.update_tilemap()
        self.update_minimap()


    def update_minimap(self):
        self.minimap_surface.fill(Color.BLACK.value)

        minimap_shape = self.minimap_surface.get_size()

        border = (5, 5)
        smallest_idx = (min(chunk[0] for chunk, data in self.chunk_renderer.chunk_data.items()), min(chunk[1] for chunk, data in self.chunk_renderer.chunk_data.items()))
        largest_idx = (max(chunk[0] for chunk, data in self.chunk_renderer.chunk_data.items()), max(chunk[1] for chunk, data in self.chunk_renderer.chunk_data.items()))



        width_space = minimap_shape[0]  - 2 * border[0]
        height_space = minimap_shape[1] - 2 * border[1]

        chunks_x = (largest_idx[1] - smallest_idx[1] + 1)
        chunks_y = (largest_idx[0] - smallest_idx[0] + 1)

        chunk_size_in_pxls_minimap = tuple([int(width_space / chunks_x),
                                           int(height_space / chunks_y)])

        player_tile = self.scroll_handler.WorldTile
        current_chunk = chunk_of_tile(player_tile, self.chunk_renderer.chunk_size)

        chunk_color_lookup = {
            (0, 0): Color.ORANGE,
        }
        if self.minimap_show_player_chunk_toggle.value:
            chunk_color_lookup[current_chunk] = Color.YELLOW

        for chunk, data in self.chunk_renderer.chunk_data.items():
            help.draw_box(surface=self.minimap_surface,
                          rect=Rectangle.from_tuple((
                              (chunk[1] - smallest_idx[1]) * chunk_size_in_pxls_minimap[0] + border[0],
                              (chunk[0] - smallest_idx[0]) * chunk_size_in_pxls_minimap[1] + border[1],
                              chunk_size_in_pxls_minimap[0],
                              chunk_size_in_pxls_minimap[1]
                          )),
                          color=chunk_color_lookup.get(chunk, Color.DARK_GRAY),
                          outline_color=Color.DIM_GRAY if self.minimap_show_chunk_outline_toggle.value else None
                          )

        # tile_color_lookup = {
        #     player_tile: Color.GREEN
        # }
        # if self.minimap_show_player_tile_toggle.value:
        #     for tile, data in self.chunk_renderer.chunk_data[current_chunk].items():
        #         help.draw_box(surface=self.minimap_surface,
        #                       rect=Rectangle(
        #                           x=(tile[1] - smallest_idx[1] * self.chunk_renderer.chunk_size[0]) * tile_size_in_pxls_minimap[0] + border[0],
        #                           y=(tile[0] - smallest_idx[0] * self.chunk_renderer.chunk_size[1]) * tile_size_in_pxls_minimap[1] + border[1],
        #                           width=tile_size_in_pxls_minimap[0],
        #                           height=tile_size_in_pxls_minimap[1]
        #                       ),
        #                       color=tile_color_lookup.get(tile, Color.DARK_GRAY),
        #                       outline_color=Color.DIM_GRAY if self.minimap_show_chunk_outline_toggle.value else None
        #                       )
        tilesize_in_minimap = (chunk_size_in_pxls_minimap[0] / self.chunk_renderer.chunk_size[0],
                               chunk_size_in_pxls_minimap[1] / self.chunk_renderer.chunk_size[1])
        smallest_tiles = (smallest_idx[0] * self.chunk_renderer.chunk_size[0], smallest_idx[1] * self.chunk_renderer.chunk_size[1])

        player_size_px = 10
        minimap_tile =((player_tile[0] - smallest_tiles[0]) * tilesize_in_minimap[1] + border[1],
                        (player_tile[1] - smallest_tiles[1]) * tilesize_in_minimap[0] + border[0])

        help.draw_circle(self.minimap_surface,
                         center=Vector2(minimap_tile[1] + player_size_px / 2, minimap_tile[0] + player_size_px / 2),
                         radius=player_size_px/2,
                         color=Color.BLUE,
                         outline_color=Color.WHITE)


    def sprite_updater(self, delta_time_ms: int):
        self.main_character.animate(delta_time_ms)

    def on_resize(self):
        pass

    def update_built_in_surfaces(self, surface_types: List[BuiltInSurfaceType]):
        pass


if __name__ == "__main__":

    game = ChunkGame()
    game.main()