from IKImage import ImageManager, fenetre, canvas, Pos2D
import tkinter as Tk
import time
import random
import math
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression

class Serpent1DDL:
    # Le serpent avec 1 seul segment et 1 seul degre de liberte
    def __init__(self):
        self.manager = ImageManager() # le manager d'images
        self.manager.ajouterImage('herbe') # fond en herbe (800x600 en 0,0)
        self.manager.ajouterImage('serpentCorpsTete') # corps du serpent avec tete (500x49)
        self.manager.setInitialPositionImage('serpentCorpsTete',50,520) # position initiale du serpent
        self.manager.setInitialRotationCenterPositionImage('serpentCorpsTete',0,24) # position du centre de rotation
        self.manager.setInitialNextRotationCenterPositionImage('serpentCorpsTete',500,24) # position de la bouche
        self.setAngle(0) # le serpent est horizontal au depart
        self.manager.ajouterImage('pomme') # l'image de la pomme (50x50)
        self.manager.setInitialPositionImage('pomme',408,198) # position de la pomme (correspond a un angle de 40 degre du serpent)

        # Interface utilisateur pour le serpent 1DDL
        # bouton pour tourner de 1 degre
        self.buttonAngleCorps = Tk.Button(fenetre,text="tourner 1 deg",command=self.changeAngleClick)
        self.buttonAngleCorps.place(x=800,y=0,width=200,height=50)
        # slider pour changer l'angle
        self.sliderAngleCorps = Tk.Scale(fenetre,from_=0,to=90,orient=Tk.HORIZONTAL,command=self.changeAngleSlider)
        self.sliderAngleCorps.place(x=800,y=50,width=200,height=50)
        # clic souris pour placer la pomme
        fenetre.bind("<ButtonRelease-1>",self.clickPositionPomme)
        # bouton de lancement de la recherche aleatoire
        self.buttonRechercheAleatoire = Tk.Button(fenetre,text="Recherche aleatoire",command=self.cherche_aleatoire)
        self.buttonRechercheAleatoire.place(x=800,y=100,width=200,height=50)
        # bouton de lancement de la recherche analytique
        self.buttonRechercheAnalytique = Tk.Button(fenetre,text="Calcul analytique",command=self.calcul_analytique)
        self.buttonRechercheAnalytique.place(x=800,y=150,width=200,height=50)
        # bouton pour lancer l'animation
        self.buttonAnimation = Tk.Button(fenetre,text="Animation",command=self.animation)
        self.buttonAnimation.place(x=800,y=200,width=200,height=50)
        # texte de statut de l'application
        self.textStatut = Tk.StringVar()
        self.labelStatut = Tk.Label(fenetre,textvariable=self.textStatut)
        self.labelStatut.place(x=800,y=500,width=200,height=50)
        self.textStatut.set("Cliquer sur un bouton de recherche ou animation")
        # lancement d'un chronometre
        self.buttonGenerateDB = Tk.Button(fenetre,text="Generate DB",command=self.generateDB)
        self.buttonGenerateDB.place(x=800,y=250,width=100,height=50)
        self.buttonInference = Tk.Button(fenetre,text="Inference",command=self.inference)
        self.buttonInference.place(x=900,y=250,width=100,height=50)
        self.debutRecherche = time.time()

    def setAngle(self,angle):
        # donne un angle au serpent
        self.manager.setLocalOrientationImage('serpentCorpsTete',angle)
        self.manager.update()

    def update(self):
        # met a jour les images et teste si la bouche est sur la pomme
        self.manager.update()
        if self.mangePomme():
            self.textStatut.set("Trouve en "+str(round(time.time() - self.debutRecherche,3))+" secondes")

    def mangePomme(self):
        # determine si la bouche est proche de la pomme
        posPomme = self.manager.getCenterPositionImage('pomme')
        posBouche = self.manager.getGlobalNextCenterPositionImage('serpentCorpsTete')
        return posBouche.distance(posPomme) < 10

    def close(self):
        # fermeture des ressources du serpent
        self.manager.close()

    def changeAngleClick(self):
        # change l'angle du serpent suite a un clic sur le bouton de rotation
        angleCorps = self.manager.getLocalOrientationImage('serpentCorpsTete')
        self.setAngle(angleCorps+1)
        self.sliderAngleCorps.set(angleCorps+1)
        self.update()

    def changeAngleSlider(self,value):
        # change l'angle du serpent suite a un changement de placement du slider
        self.setAngle(int(value))
        self.update()

    def clickPositionPomme(self,event):
        # change la position de la pomme suite a un clic
        posxPommeFenetre = event.x_root-fenetre.winfo_rootx()
        posyPommeFenetre = event.y_root-fenetre.winfo_rooty()
        if posxPommeFenetre < 775:
            self.manager.setCenterPositionImage('pomme',posxPommeFenetre,posyPommeFenetre)
            self.update()

    def cherche_aleatoire(self):
        # cherche l'angle du serpent de maniere aleatoire
        self.debutRecherche = time.time()
        iteration = 0

        # TODO : Donner une valeur assez grande a iteration_max
        iteration_max = 1

        while not self.mangePomme() and iteration < iteration_max:
            iteration += 1
            self.textStatut.set("En recherche aleatoire..."+str(iteration))

            # TODO : tirer aleatoirement une valeur pour la variable angle
            # Indication : vous pouvez utiliser la fonction random.randint(a,b) qui retourne une valeur entre a et b
            angle = 0

            self.setAngle(angle)
            self.sliderAngleCorps.set(angle)
            self.update()
            fenetre.update()
        if iteration==iteration_max:
            self.textStatut.set("Nombre d'iterations max atteint")


    def calcul_analytique(self):
        # calcul l'angle du serpent 1DDL pour atteindre la pomme
        self.textStatut.set("En recherche analytique...")
        self.debutRecherche = time.time()

        # TODO : recuperation de la position de la pomme P avec la fonction getCenterPositionImage
        # TODO : recuperation de la position du centre de rotation P1 avec la fonction getGlobalCenterPositionImage
        # TODO : calcul et normalisation (fonction normalize) du vecteur P1P
        # TODO : calcul de l'angle en degre avec les fonctions math.degrees et math.acos
        angle = 0

        # mise a jour affichage
        self.setAngle(angle)
        self.sliderAngleCorps.set(angle)
        self.update()
        fenetre.update()

    def generateDB(self):
        # genere la base de donnees
        DBentrees = []
        DBsorties = []
        # TODO : Tester un nombre de configurations différents et voir si le fait d'agrandir la base améliore le modèle
        nbConfigurations = 10
        # calcul des configurations a enregistrer pour la base de données
        for c in range(nbConfigurations):
            self.textStatut.set("Creation DB... "+str(c)+"/"+str(nbConfigurations))
            # position de la pomme aléatoire dans un cercle autour du serpent
            dirPomme = random.randint(0,157) / 100.0
            posPommex = math.floor(50+math.cos(dirPomme)*500)
            posPommey = math.floor(544-math.sin(dirPomme)*500)
            # TODO: placer la pomme aux coordonnées posPommex et posPommey, (fonction self.manager.setCenterPositionImage)
            self.manager.setCenterPositionImage("pomme",0,0)
            # test de tous les angles, on enregistre que ceux qui correspondent à une bonne réponse
            for angle in range(90):
                self.setAngle(angle)
                # si le serpent peut manger la pomme,
                if self.mangePomme():
                    # TODO: alors on ajoute à DBentrees la liste [posPommex,posPommey] (fonction append)
                    DBentrees.append()
                    # TODO: et on ajoute l'angle à DBsorties (fonction append)
                    DBsorties.append()
        # apprentissage du modele (regression lineaire)
        self.textStatut.set("Apprentissage du modele...")
        self.pipeline = Pipeline([("scaler", StandardScaler()),("model", LinearRegression())])
        # TODO : entrainer le pipeline via la fonction fit(np.array(entrées)),np.array(sorties))
        # TODO : tester l'entrainement avec d'autres modèles que la regréssion linéaire
        self.textStatut.set("Apprentissage du modele... OK")

    def inference(self):
        self.debutRecherche = time.time()
        # predit une solution (angle) avec le modèle
        self.textStatut.set("Prediction de l'angle...")
        # TODO: récupérer la position de la pomme
        posPomme = 0
        # TODO: créer la variable entrees à partir de la position de la pomme (fonction np.array())
        entrees = np.array([[0,0]])
        # TODO: prédire avec la sortie avec le modèle (fonction predict), et utiliser la fonction math.floor pour arrondir la sortie
        sortiePredite = 0
        self.textStatut.set("Prediction de l'angle... OK")

        # update la fenêtre et l'angle
        self.setAngle(sortiePredite)
        self.sliderAngleCorps.set(sortiePredite)
        self.update()
        fenetre.update()

    def animation(self):
        i = 0
        while i < math.pi / 2:
            # deplace la pomme et demande au serpent de la suivre
            posPommex = 50+math.cos(i)*500
            posPommey = 544-math.sin(i)*500
            deltax = math.sin(i*8)*20
            deltay = -math.sin(i*8)*20
            self.manager.setCenterPositionImage('pomme',posPommex+deltax,posPommey+deltay)
            # calcul l'angle
            self.calcul_analytique()
            # mise a jour affichage
            self.update()
            fenetre.update()
            i += 0.01

s = Serpent1DDL()
fenetre.update()
fenetre.mainloop()
s.close()
