import pygame, os, math, time
import gamedata.definitions as lib
import gamedata.scenes as scenes
import gamedata.objects.gameloop as gameloop
from gamedata.objects.particles import Particle

class Game():
    def __init__(self):

        self.DISPLAY_WIDTH, self.DISPLAY_HEIGHT = 1280, 720
        self.window = pygame.Surface((self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT))
        self.realwindow = pygame.Surface((self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT))
        self.screen = pygame.display.set_mode((self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT),pygame.RESIZABLE)
        self.screenw,self.screenh = self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT
        self.resizescreenw,self.resizescreenh = self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT
        self.screenoffx,self.screenoffy = 0,0
        pygame.display.set_caption("Pinmik Panik !")
        pygame.init()
        pygame.mixer.init()

        font = "gamedata/font.ttf"
        self.fontfile = pygame.font.Font(font,32)
        self.fontfilesmall = pygame.font.Font(font,16)
        self.fontfilebig = pygame.font.Font(font,64)

        self.logs = []

        self.running = True
        self.init_inputs()
        self.gameloop = gameloop.GameLoop() # Je crée une boucle de jeu
        def load_image(filename):
            return pygame.image.load(filename).convert_alpha()
        self.sprite_lib = self.init_assets("gamedata/assets/",load_image) # Dico qui contient chaques images du dossier assets
        pygame.display.set_icon(self.sprite_lib["icon.png"])
        self.sound_lib = self.init_assets("gamedata/sounds/",pygame.mixer.Sound) # Pareil, mais pour les musiques / sons

        self.sound_volumes = {}
        for i in self.sound_lib.keys():
            self.sound_volumes[i] = 1
        self.spriteLists = {} # Tiendra des listes préarrangées

        self.scenes = scenes  # Je stoque mes modules à l'intérieur de ma classe pour y avoir accès partout
        self.scene = None
        self.lib = lib
        self.pygame = pygame
        self.math = math

        self.elapsedtime = 0

        self.datadir = lib.get_save_dir("pinmikpanik")
        self.dataname = "highscore.ini"
        self.settingsname = "settings.ini"

        highscore = lib.loadscore(self.datadir,self.dataname)
        bgm,sfx = lib.loadsettings(self.datadir,self.settingsname)
        bgm,sfx = bgm/100,sfx/100

        self.globals = {} # Un dico pour ranger toute les valeurs globales, pour communiquer entre objets par exemples
        self.globals["camerax"] = 0
        self.globals["cameray"] = 0
        self.globals["scamerax"] = 3
        self.globals["scameray"] = 0
        self.globals["debug"] = False
        self.globals["highscore"] = highscore
        self.globals["pause"] = False
        self.scaleCamera()

        self.globals["bgmvolume"] = bgm
        self.globals["sfxvolume"] = sfx

        self.reinit_volumes()

        self.pasttime = time.time()

        # Je charge la scene de base
        scenes.main(self)

    def set_camera(self,posx,posy):
        self.globals["camerax"], self.globals["cameray"] = posx,posy

    def log(*args):
        args[0].logs.append(" ".join(args[1:]))
        print(" ".join(args[1:]))

    def reinit_volumes(self):

        for i in self.sound_lib.keys(): # J'applique de base les volumes
            if i.startswith("sfx/"):
                self.sound_lib[i].set_volume(self.globals["sfxvolume"]*self.sound_volumes[i])
            if i.startswith("bgm/"):
                self.sound_lib[i].set_volume(self.globals["bgmvolume"]*self.sound_volumes[i])

    def set_volume(self,sound,newvolume):
        self.sound_volumes[sound] = newvolume
        self.reinit_volumes()

    def game_loop(self):

        self.dt = time.time()-self.pasttime
        self.elapsedtime += self.dt
        self.pasttime = time.time()

        self.check_events() # Détecte les entrées clavier
        self.window.fill((0)*3) # Remplis l'écran de noir
        if self.gameloop: # Si j'ai une boucle de jeu, la lancer
            self.gameloop.step(self) # La logique de la boucle
            self.gameloop.draw(self) # L'affichage de la boucle
        if self.scene :
            self.scene(self)
            self.scene = False
        pygame.display.update() # Mettre à jour l'affichage


    def init_inputs(self):

        self.inputs = {}
        self.inputs["unicode"] = ""
        self.inputs["mouse"] = {
            "pos" : [0,0], # Position
            "rel" : [0,0], # Mouvement relatif
            "click" : 0 # Timer du click
        }
        self.inputs["keys"] = {
            "escape":{
                "timer" : 0, # Timer de la touche
                "realtime" : 0, # Temps réel préssé
                "pressed" : False,
                "keycode" : pygame.K_ESCAPE # Code pygame de la touche en question
            }
        }

        self.no_unicode = [pygame.K_ESCAPE,pygame.K_BACKSPACE,pygame.K_RETURN]


    def check_events(self):
        self.inputs["unicode"] = ""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            if event.type == pygame.VIDEORESIZE:
                self.screenw,self.screenh = int(event.w),int(event.h)
                self.screen = pygame.display.set_mode((self.screenw,self.screenh),pygame.RESIZABLE)
            if event.type == pygame.KEYDOWN:
                for i in self.inputs["keys"].keys():
                    if event.key == self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée
                        self.inputs["keys"][i]["pressed"] = True
                    else:
                        if event.key not in self.no_unicode:
                            self.inputs["unicode"] = event.unicode # Je récupère la "lettre" de la touche préssée
            if event.type == pygame.KEYUP:
                for i in self.inputs["keys"].keys():
                    if event.key == self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée
                        self.inputs["keys"][i]["pressed"] = False

        x,y = pygame.mouse.get_pos()
        x-=self.screenoffx
        y-=self.screenoffy
        x/=self.resizescreenw/self.DISPLAY_WIDTH
        y/=self.resizescreenh/self.DISPLAY_HEIGHT
        self.inputs["mouse"]["pos"] = x,y

        self.inputs["mouse"]["rel"] = pygame.mouse.get_rel() # Déplacement par rapport à la frame précédente
        # Getting mouse position on camera
        mx = self.inputs["mouse"]["pos"][0]*self.globals["cameraw"]/self.DISPLAY_WIDTH+self.globals["camerax"]
        my = self.inputs["mouse"]["pos"][1]*self.globals["camerah"]/self.DISPLAY_HEIGHT+self.globals["cameray"]
        self.inputs["mouse"]["campos"] = [mx,my]

        # Augmente le timer si la touche est préssée, le reset sinon
        for i in self.inputs["keys"].keys():
            if self.inputs["keys"][i]["pressed"]:
                self.inputs["keys"][i]["timer"]+=1
                self.inputs["keys"][i]["realtime"]+=self.dt
            else:
                self.inputs["keys"][i]["timer"]=0
                self.inputs["keys"][i]["realtime"]=0
        # Timer du click
        boutons  = pygame.mouse.get_pressed()
        if boutons[0]: # Si click gauche
            self.inputs["mouse"]["click"]+=1
        else:
            self.inputs["mouse"]["click"] = 0

    def init_assets(self,path,function,recursive=True):
        dico = {}
        self.scan_dir(path,path,dico,function,recursive)
        return dico

    def scan_dir(self,dirpath,origin,dico,function,recursive=True):
        scanner = os.scandir(path=dirpath)
        for i in scanner: # Je passe à travers toutes les données d'un dossier, fichiers et sous dossiers compris
            # i.path est le chemin du fichier, par exemple
            # Si c'est une image, je l'importe et l'ajoute à la librairie
            finalpath = i.path.replace("\\","/")
            if i.is_file() or not recursive:
                finalpath = finalpath.replace(origin,'') # J'enleve l'origine (dans ce cas 'assets/' car c'est redontant, tout les sprites sont dedans
                dico[finalpath] = function(i.path)
            # Si c'est un dossier, je répete l'opération mais à l'intérieur de celui ci
            if i.is_dir() and recursive:
                self.scan_dir(i.path,origin,dico,function,recursive)
        scanner.close()

    def getSpriteDir(self,directory,ext=".png",assetdir="sprite_lib"):
        keys = (directory,ext,assetdir)
        assetdir = getattr(self,assetdir) # Si je ne précise pas quel type de ressource, je cherche dans mes sprites
        if keys in self.spriteLists.keys():
            return self.spriteLists[keys]
        else:
            # Organise les sprites
            sprite_list = []
            index = 0
            while directory+str(index)+ext in assetdir.keys():
                sprite_list.append(assetdir[directory+str(index)+ext])
                index+=1
            # Le stoque pour éviter de les réorganiser à chaque fois
            self.spriteLists[keys] = sprite_list

            return sprite_list

    def addParticle(self,sprites,posx,posy,velx=0,vely=0,modvelx=0,modvely=0,flipx=False,flipy=False,fps=15):
        p = Particle(self,sprites,posx,posy,velx,vely,modvelx,modvely,flipx,flipy,fps)
        self.gameloop.summon(p)

    def scaleCamera(self,neww=None,newh=None):
        if not neww:
            neww=self.DISPLAY_WIDTH
        if not newh:
            newh=self.DISPLAY_HEIGHT
        self.globals["cameraw"] = neww
        self.globals["camerah"] = newh

        self.globals["tempsubsurface"] = pygame.Surface((neww,newh))