#!/usr/bin/python3

import random
from units import rat, bomb
import uuid
from engine import maze, sdl2 as engine, controls
import os
import datetime
import json

class MiceMaze(controls.KeyBindings):
    def __init__(self, maze_file):
        self.map = maze.Map(maze_file)
        self.audio = True
        self.cell_size = 40
        self.full_screen = False
        self.engine = engine.GameWindow(self.map.width, self.map.height, 
                                        self.cell_size, "Mice!", 
                                        key_callback=(self.key_pressed, self.key_released, self.axis_scroll))
        self.pointer = (random.randint(1, self.map.width-2), random.randint(1, self.map.height-2))
        self.scroll_cursor()
        self.points = 0
        self.graphics_load()
        self.units = {}
        self.unit_positions = {}
        self.unit_positions_before = {}
        self.scrolling_direction = None
        self.game_status = "start_menu"
        self.game_end = (False, None)
        self.scrolling = False
        self.sounds = {}
        self.start_game()
        self.background_texture = None
        self.configs = self.get_config()
        
    def get_config(self):
        configs = {}
        for file in os.listdir("conf"):
            if file.endswith(".json"):
                with open(os.path.join("conf", file)) as f:
                    configs[file[:-5]] = json.load(f)
        return configs
    
    def start_game(self):
        for _ in range(5):
            self.new_rat()
            
    def count_rats(self):
        count = 0
        for unit in self.units.values():
            if isinstance(unit, rat.Rat):
                count += 1
        return count
            
    def new_rat(self, position=None):
        if position is None:
            position = self.choose_start()
        rat_class = rat.Male if random.random() < 0.5 else rat.Female
        self.spawn_unit(rat_class, position)
        
    def spawn_bomb(self, position):
        self.spawn_unit(bomb.Timer, position)
    
    def spawn_unit(self, unit, position, **kwargs):
        id = uuid.uuid4()
        self.units[id] = unit(self, position, id, **kwargs)
        
    def choose_start(self):
        if not hasattr(self, '_valid_positions'):
            self._valid_positions = [
                (x, y) for y in range(1, self.map.height-1) 
                for x in range(1, self.map.width-1) 
                if self.map.matrix[y][x]
            ]
        return random.choice(self._valid_positions)

    def draw_maze(self):
        if self.background_texture is None:
            
            texture_tiles = []
            for y, row in enumerate(self.map.matrix):
                for x, cell in enumerate(row):
                    variant = x*y % 4
                    tile = self.grasses[variant] if cell else self.tunnel
                    texture_tiles.append((tile, x*self.cell_size, y*self.cell_size))
            self.background_texture = self.engine.create_texture(texture_tiles)
        self.engine.draw_background(self.background_texture)
        
    def game_over(self):
        if self.game_end[0]:
            if not self.game_end[1]:
                self.engine.dialog("Game Over: Mice are too many!", image=self.assets["BMP_WEWIN"])
            else:
                self.engine.dialog(f"You Win! Points: {self.points}", image=self.assets["BMP_WEWIN"], scores=self.read_score())
                
                
            return True
        
        if self.count_rats() > 200:
            self.stop_sound()
            self.play_sound("WEWIN.WAV")
            self.game_end = (True, False)
            self.game_status = "paused"
            return True
        if not len(self.units):
            self.stop_sound()
            self.play_sound("VICTORY.WAV")
            self.play_sound("WELLDONE.WAV", tag="effects")
            self.game_end = (True, True)
            self.game_status = "paused"
            self.save_score()
            return True
        
    def save_score(self):
        with open("scores.txt", "a") as f:
            f.write(f"{datetime.datetime.now()} - {self.points}\n")
    
    def read_score(self):
        table = []
        with open("scores.txt") as f:
            rows = f.read().splitlines()
            for row in rows:
                table.append(row.split(" - "))
            table.sort(key=lambda x: int(x[1]), reverse=True)
        return table
            
    def update_maze(self):
        if self.game_over():
            return
        if self.game_status == "paused":
            self.engine.dialog("Pause")
            return
        if self.game_status == "start_menu":
            self.engine.dialog("Welcome to the Mice!", subtitle="A game by Matteo because he was bored",image=self.assets["BMP_WEWIN"])
            return
        self.engine.delete_tag("unit")
        self.engine.delete_tag("effect")
        self.engine.draw_pointer(self.pointer[0] * self.cell_size, self.pointer[1] * self.cell_size)
        self.unit_positions.clear()
        self.unit_positions_before.clear()
        for unit in self.units.values():
            self.unit_positions.setdefault(unit.position, []).append(unit)
            self.unit_positions_before.setdefault(unit.position_before, []).append(unit)
        for unit in self.units.copy().values():
            unit.move()
            unit.collisions()
            unit.draw()
        self.engine.update_status(f"Mice: {self.count_rats()} - Points: {self.points}")
        self.scroll()
        self.engine.new_cycle(50, self.update_maze)


    def run(self):
        self.engine.mainloop(update=self.update_maze, bg_update=self.draw_maze)
            
    def scroll_cursor(self, x=0, y=0):
        if self.pointer[0] + x > self.map.width or self.pointer[1] + y > self.map.height:
            return
        
        self.pointer = (
            max(1, min(self.map.width-2, self.pointer[0] + x)),
            max(1, min(self.map.height-2, self.pointer[1] + y))
        )
        self.engine.scroll_view(self.pointer)

    def play_sound(self, sound_file,tag="base"):
        self.engine.play_sound(sound_file, tag=tag)
    
    def stop_sound(self, tag=None):
        self.engine.stop_sound()

    def graphics_load(self):
        self.tunnel = self.engine.load_image("Rat/BMP_TUNNEL.png", surface=True)
        self.grasses = [self.engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png", surface=True) for i in range(4)]
        self.rat_assets = {}
        self.bomb_assets = {}
        for sex in ["MALE", "FEMALE", "BABY"]:
            self.rat_assets[sex] = {}
            for direction in ["UP", "DOWN", "LEFT", "RIGHT"]:
                self.rat_assets[sex][direction] = self.engine.load_image(f"Rat/BMP_{sex}_{direction}.png", transparent_color=(128, 128, 128))
        for n in range(5):
            self.bomb_assets[n] = self.engine.load_image(f"Rat/BMP_BOMB{n}.png", transparent_color=(128, 128, 128))
        self.assets = {}
        for file in os.listdir("assets/Rat"):
            if file.endswith(".png"):
                self.assets[file[:-4]] = self.engine.load_image(f"Rat/{file}")
                
    def add_point(self, value):
        self.points += value

        
if __name__ == "__main__":
    
    solver = MiceMaze('maze.json')
    solver.run()

