#from __future__ import unicode_literals
# -*- coding: utf-8 -*-
from __future__ import division
import os

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from outilsSvg import *
from Terrain import Terrain

class Case(QGraphicsPolygonItem):
    """objet graphique representant une case du plateau"""
    def __init__(self, plateau, parent=None):
        super(Case, self).__init__(parent)
        #plateau
        self.plateau = plateau
        
        #attributs
        self.x = 0
        self.y = 0        
        self.altitude = 0                   
        self.terrain = Terrain()                       #terrain par defaut 
        self.couleur = None                            #couleur du fond par defaut
        self.bordure = QColor(85, 85, 85, 85)          #couleur de la bordure par defaut
        self.centreGraphique = None

        self.occupeePar = {}    #objet: altitudes occupees (sous forme de tuple, ex: (0,1,2) pour une creature occupant les cases d'altitude 0, 1 et 2)
        self.estCache = False   #est sous un cache place par le MJ (cache le terrain, les decors, les pions aux joueurs...)

        #effet sur la case 
        self.effetActif = "" 

        #polygones d'affichage
        self.polygoneEffet = None
        self.polygoneAffichageSpecial = None
        self.etiquetteAltitude = None
        

    def __getstate__(self):
        """selectionne les attributs qui seront sauvegardes"""
        self.idTerrain = self.terrain.id
        state = {key:value for key, value in self.__dict__.items() if key in ["x", "y", "altitude","idTerrain","bordure","couleur", \
                                                                              "estCache", "ombre", "effetActif"]}

        return (state)

    def __setstate__(self, state):
        """recupere les attributs sauvegardes"""
        self.__dict__ = state
        if self.idTerrain == "00":
            self.terrain = Terrain()
        else:
            self.terrain = charger("lib\\biblio\\terrain", self.idTerrain)
        
    ######## fonctions de base : geometrie, aspect graphique      ######
    def creer(self, x, y, couleur = QColor(0, 255, 0, 80)):
        """creation du polygone et enregistrement des donnees geometriques"""
        self.x = x
        self.y = y
        self.couleur = couleur
        #creation de l'objet graphique sur le plateau
        self.creerGraphique()
        
    def creerGraphique(self):
        """cree les objets graphiques representant la case"""
        #reinitialisation des variables 
        self.couleurEffet = {"brule":QColor("orange"), "glace":QColor("white"), "poison":QColor("green"), "eau":QColor("blue")}
        self.imgEffet = {"brule":"effFeu.jpg", "glace":"effGlace.jpg", "poison":"effPoison.png", "eau":"effEau.png"}

        #enregistrement des cases voisines: 
        self.voisins = self.lstVoisins(self.x, self.y)
        self.occupeePar = {}
   
        #enregistrement du centre
        if self.plateau.formeCases == "H":  #refPlateau
            k = 0
            if 1 == (self.x % 2): k = 0.5
            self.centreGraphique = QPointF(((self.x*0.866)+0.5773)*self.plateau.hCase, (self.y+k+0.5)*self.plateau.hCase)
        else:
            self.centreGraphique = QPointF((self.x+0.5)*self.plateau.hCase, (self.y+0.5)*self.plateau.hCase)
        
        #cree le polygone de la case
        self.setPolygon(self.polygone(self.x, self.y))
        self.plateau.addItem(self)  #refPlateau
        
        #interactions graphiques:
        self.setFlag(QGraphicsItem.ItemIsFocusable)
        self.setAcceptHoverEvents(True)
        self.setAcceptDrops(True)
        self.setZValue(0)
        self.setFiltersChildEvents(True)

##        #pour afficher les coordonnees des cases:        
##        text = QGraphicsSimpleTextItem("{}-{}".format(self.x,self.y), parent=self)
##        if self.plateau.formeCases == "H":
##            text.setPos(QPointF(((self.x*0.866)+0.2886)*self.plateau.hCase,  self.y*self.plateau.hCase))
##        else:
##            text.setPos(QPointF(self.x*self.plateau.hCase,  self.y*self.plateau.hCase))

        self.logoDep = LogoDep(self)
        self.logoDep.creer()
        
        #apparence initiale de la case
        self.majCouleur(self.couleur)            

        #polygone utilise pour afficher la cible du curseur
        self.polygoneCible = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self)
        self.polygoneCible.setVisible(False)
        self.polygoneCible.setAcceptHoverEvents(False)

    def recreer(self, plateau):
##        self.plateau = plateau  #refPlateau
        self.plateau = plateau
        super(Case, self).__init__()
        #polygones d'affichage
        self.polygoneEffet = None
        self.polygoneAffichageSpecial = None
        self.etiquetteAltitude = None
        
        self.creerGraphique()
        if self.terrain:
            if len(self.terrain.nom) > 0 : 
                self.majTerrain(self.terrain)   
        self.majAffichageSpecial("")
        self.majEffet(self.effetActif)

    def polygone(self, x, y):
        """renvoie l'objet graphique hexagone de la case"""
        polygone = QPolygonF()
        if self.plateau.formeCases == "H":
            #si x est impair sur un plateau a cases hexagonales, le y est augmente de 0.5
            if 1 == (x % 2):
                y += 0.5
            polygone  << QPointF(((x*0.866)+0.2886)*self.plateau.hCase,  y*self.plateau.hCase) \
                      << QPointF(((x*0.866)+0.866)*self.plateau.hCase,   y*self.plateau.hCase) \
                      << QPointF(((x*0.866)+1.1547)*self.plateau.hCase, (y+0.5)*self.plateau.hCase) \
                      << QPointF(((x*0.866)+0.866)*self.plateau.hCase,  (y+1)*self.plateau.hCase) \
                      << QPointF(((x*0.866)+0.2886)*self.plateau.hCase, (y+1)*self.plateau.hCase)  \
                      << QPointF( (x*0.866)*self.plateau.hCase,         (y+0.5)*self.plateau.hCase)
        else:
            polygone  << QPointF(x*self.plateau.hCase,      y*self.plateau.hCase) \
                      << QPointF((x+1)*self.plateau.hCase,  y*self.plateau.hCase) \
                      << QPointF((x+1)*self.plateau.hCase,  (y+1)*self.plateau.hCase) \
                      << QPointF(x*self.plateau.hCase,      (y+1)*self.plateau.hCase)          
        return polygone

    def lstVoisins(self, x, y):
        """renvoie la liste des cases voisines
           seulement cases existantes sur le plateau / seulement cases adjacentes (cas des cases carrees)"""
        voisins = []
        if self.plateau.formeCases == "H":
            if 1 == (x % 2):
                lst = [(x, y-1), (x+1, y), (x+1, y+1), (x,  y+1), (x-1, y+1), (x-1, y)]
            else:
                lst = [(x, y-1), (x+1, y-1), (x+1, y), (x,  y+1), (x-1, y), (x-1, y-1)]
        else:
            lst = [(x, y-1), (x+1, y-1), (x+1, y), (x+1, y+1), (x,   y+1), (x-1, y+1), (x-1, y), (x-1, y-1)]
                  
        for coord in lst: 
            if (coord[0] >= 0 and coord[1] >= 0 and coord[0] < self.plateau.nbCasesX and coord[1] < self.plateau.nbCasesY):
                voisins.append(coord)
        return voisins                      

     
    ########################

    ### deplacement
    def estFranchissable(self, z=0):
        """la case est-elle franchissable?"""
        retour = True
        if self.terrain.franchissable == False:
            retour = False
        else:
            if self.estOccupee(z) == True:
                retour = False
        return retour

    def coutDep(self):
        #a implementer, en fonction des
        #capacites de deplacement du pion actuellement selectionne
        return 1

    ### occupation
    def majOccupation(self, objet, nouveauZ = None):
        """met a jour l'occupation de la case par les pions, decors..."""
        if objet != None:
            if not objet in self.occupeePar and nouveauZ != None:
                #on ajoute l'objet a la liste des objets occupant la case ou on met a jour son altitude
                casesOccupees = []
                for i in range(0, objet.hauteur):
                    casesOccupees.append(self.altitude + nouveauZ + i)
                self.occupeePar[objet] = casesOccupees
                
            elif objet in self.occupeePar and nouveauZ == None:
                #on supprime l'objet de la liste des objets occupant la case
                del self.occupeePar[objet]
            else:
                pass
            
    def estOccupee(self, z=0):
        """renvoie vrai si la case correspondant a la hauteur z est occupee"""
        retour = False
        for objet in self.occupeePar:
            if objet.hauteur > 0 and z in self.occupeePar[objet]:
                retour = True
        return retour        

    def estOccupeePar(self, z=0):
        """si la case correspondant a la hauteur z est occupee, renvoie l'objet en question
           sinon renvoie None"""
        retour = None
        for objet in self.occupeePar:
            if objet.hauteur > 0 and z in self.occupeePar[objet]:
                retour = objet
        return retour  

    def pionOccupant(self):
        """si un pion occupe cette case, le renvoie"""
        retour = None
        for objet in self.occupeePar:
            if objet.__class__.__name__ == "Combattant":
                retour = objet
        return retour          


    #autres interaction de combat
    def estObstacleVision(self, hauteurObs):
        """renvoie vrai si la case et l'eventuel decor qui l'occupe bloquent le champ de
           vision d'un observateur situe a la hauteur precisee"""
        retour = False
        if self.altitude > hauteurObs:
            retour = True
        else:
            hauteurObstacle = 0
            for objet in self.occupeePar:
                if objet.hauteur > hauteurObstacle:
                    hauteurObstacle = objet.hauteur
            if (self.altitude + hauteurObstacle) > hauteurObs:
                retour = True
        return retour                

    ########## fonctions de maj ###########
    def majCouleur(self, couleur):
        self.couleur = couleur
        self.setBrush(couleur)
        pinceau = QPen()
        pinceau.setColor(self.couleurBordure())
        pinceau.setWidth(1)
        self.setPen(pinceau)
        
    def majTerrain(self, terrain):
        if terrain != None: 
            self.terrain = terrain
            if len(self.terrain.imgTexture) > 0:
                self.setBrush(QBrush(QImage("img\\"+self.terrain.imgTexture)))
            else:
                if self.terrain.couleur.isValid():
                     self.setBrush(QBrush(self.terrain.couleur))
                     self.couleur = self.terrain.couleur
        else:
            self.terrain = Terrain()

    def majAffichageDeplacement(self, cout, valide = True):
        """lorsque cette case est sur le chemin d'un pion,
            un disque blanc ou rouge apparait indiquant le
            cout du deplacement jusqua cette case
            un cout de 0 fait disparaitre ce symbole"""
        self.logoDep.afficher(cout, valide)

    def majEstCibleCurseur(self, actif, valide=True):
        """affiche la case comme etant la cible du curseur (lors d'une creation de pion, d'un deplacement...)"""
        if actif:
            pinceau = QPen()
            pinceau.setWidth(5)
            brush = QBrush()
            brush.setStyle(13)
            pinceau.setColor(self.couleurProj(valide))
            brush.setColor(self.couleurProj(valide))
            self.polygoneCible.setPen(pinceau)
            self.polygoneCible.setBrush(brush)
            self.setZValue(99)
        else:
            self.setZValue(0)
        self.polygoneCible.setVisible(actif)    
            
    def majEffet(self, effet):
        """met a jour l'effet actif sur la case"""       
        if self.polygoneEffet == None:
            #on cree le polygone utilise pour afficher les effets
            self.polygoneEffet = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self)
            pinceau = QPen()
            pinceau.setColor(self.bordure)
            pinceau.setWidth(1)
            self.polygoneEffet.setPen(pinceau)
            self.polygoneEffet.setVisible(False)

        if len(effet) > 0 and effet != "aucun":
            #gradient de couleur
            gradient = QRadialGradient(self.centreGraphique, 0.5*self.plateau.hCase)
            couleur0 = QColor(0,0,0,0)
            couleur20 = self.couleurEffet[effet]
            couleur20.setAlpha(70)
            couleur10 = self.couleurEffet[effet]
            couleur10.setAlpha(255)

            gradient.setColorAt(0, couleur0)
            gradient.setColorAt(0.8, couleur0)
            gradient.setColorAt(0.95, couleur10)
            gradient.setColorAt(0.98, couleur20)
            gradient.setColorAt(1, couleur10)
            self.polygoneEffet.setBrush(gradient)
   
            self.polygoneEffet.setVisible(True)

            self.effetActif = effet
        else:
            self.polygoneEffet.setVisible(False)              

    def majAltitude(self, altitude):
        """met a jour l'altitude de la case"""
        self.altitude = altitude

    def majAffichageSpecial(self, affichage=""):
        """donne a la case l'aspect demande en rendant visible/invisible le polygone d'affichage special"""
        #polygone d'affichage special (altitude, tactique...)
        if self.polygoneAffichageSpecial == None:
            self.polygoneAffichageSpecial = QGraphicsPolygonItem(self.polygone(self.x, self.y), parent=self)
            pinceau = QPen()
            pinceau.setColor(self.bordure)
            pinceau.setWidth(1)
            self.polygoneAffichageSpecial.setPen(pinceau)

        if affichage != "altitude" and self.etiquetteAltitude:
            self.etiquetteAltitude.setVisible(False)
            
        if affichage == "tactique":
            if self.terrain.franchissable:
                self.polygoneAffichageSpecial.setBrush(QColor(255,255,255))
            else:
                self.polygoneAffichageSpecial.setBrush(QColor(50,50,50))
            self.polygoneAffichageSpecial.setVisible(True)
            
        elif affichage == "altitude":
            if self.etiquetteAltitude == None:
                self.etiquetteAltitude = QGraphicsSimpleTextItem("{}".format(self.altitude), parent=self)
                police = QFont("Georgia", 18)
                police.setItalic(True)
                self.etiquetteAltitude.setFont(police)
                self.etiquetteAltitude.setPos(QPointF(((self.x*0.866)+0.65)*self.plateau.hCase,  (self.y+0.7)*self.plateau.hCase))
            if self.altitude >= 0:
                couleur = QColor("red").lighter(200-(5*self.altitude))
            else:
                couleur = QColor("purple").lighter(200+(5*self.altitude))
            self.polygoneAffichageSpecial.setBrush(couleur)
            self.polygoneAffichageSpecial.setZValue(5)
            self.polygoneAffichageSpecial.setVisible(True)
            self.etiquetteAltitude.setText(QString.fromUtf8("{}".format(self.altitude)))
            self.etiquetteAltitude.setVisible(True)
            self.etiquetteAltitude.setZValue(6)
        else:
            self.polygoneAffichageSpecial.setVisible(False)


    #fonctions secondaires
    def couleurDep(self):
        """renvoie une couleur secondaire utilisee pour les champs de deplacements"""
        luminositeActuelle = self.couleurEffective().lightness()
        if luminositeActuelle < 150:
            retour = QColor(210,255,255,100)
        else:
            retour = QColor(210,255,255,100)
        return retour

    def couleurProj(self, valide):
        """renvoie une couleur secondaire utilisee pour les projections de deplacement"""
        luminositeActuelle = self.couleurEffective().lightness()
        if valide:
            retour = QColor("white")
        else:
            retour = QColor("red")
        if luminositeActuelle > 220:
            retour = retour.darker(120)
            
        return retour

    def couleurBordure(self):
        """renvoie la couleur utilisee pour les bordures de la case"""
        luminositeActuelle = self.couleurEffective().lightness()
        if luminositeActuelle > 150:
            retour = QColor(85, 85, 85, 130)
        elif luminositeActuelle > 100 and luminositeActuelle <= 150:
            retour = QColor(10, 10, 10, 130)
        else:
            retour = QColor(255, 255, 255, 180)
        return retour        

    def couleurEffective(self):
        """renvoie la couleur effective de la case (utile quand la texture est une image)"""
        texture = self.brush()
        if texture.textureImage().isNull():
            couleurFond = texture.color()
        else:
            couleurFond = self.couleurDominante(texture.textureImage())
        return couleurFond

    def couleurDominante(self, img):
        """retourne la couleur dominante d'une QImage"""
        r = 0
        v = 0
        b = 0
        for i in range(0, img.width()):
            for j in range(0, img.height()):
                pixel = img.pixel(i,j)
                r += qRed(pixel)
                v += qGreen(pixel)
                b += qBlue(pixel)
        nb_pix = img.width() * img.height()   
        r_moy = int(r / nb_pix)
        v_moy = int(v / nb_pix)
        b_moy = int(b / nb_pix)
        return QColor(r_moy, v_moy, b_moy)

    ######### evenements souris et clavier    #################

    def mousePressEvent(self, event):
        """evenement lors du clic souris"""
        super(Case, self).mousePressEvent(event)
        if event.button() == 1: #sur clic gauche
            accepte = self.plateau.caseCliquee(self.x, self.y)  #refPlateau
            if accepte: event.accept()
        else:
            event.ignore()

    def hoverEnterEvent(self, event):
        """met a jour l'affichage de la case au survol de la souris, si un pion est selectionne"""
        super(Case, self).hoverEnterEvent(event)
        self.plateau.caseSurvol(self.x, self.y)

    def hoverLeaveEvent(self, event):
        """met a jour l'affichage de la case au survol de la souris, si un pion est selectionne"""
        super(Case, self).hoverLeaveEvent(event)
    
    ########################


class LogoDep(QGraphicsEllipseItem):
    def __init__(self, parent = None):
        super(LogoDep, self).__init__()
        self.parent = parent
        
        self.couleur = None
        self.txt = None

    def creer(self):
        
        hC = self.parent.plateau.hCase
        rect = QRectF()
        rayon = 0.4*hC
        rect.setTopLeft(QPointF(self.parent.centreGraphique.x() - rayon, self.parent.centreGraphique.y() - rayon))
        rect.setBottomRight(QPointF(self.parent.centreGraphique.x() + rayon, self.parent.centreGraphique.y() + rayon))

        self.setRect(rect)
        

        self.txt = QGraphicsTextItem(self)
        police = QFont("Georgia", 24)
        police.setBold(True)
        self.txt.setFont(police)
        self.txt.setParentItem(self)
        self.txt.setPos(QPointF(self.parent.centreGraphique.x() - rayon, self.parent.centreGraphique.y() - 0.6*rayon))

        self.txt.setTextWidth(2*rayon)
        self.txt.setPlainText(QString.fromUtf8(""))
        self.centrerTexte()

        self.setVisible(False)
        self.setAcceptHoverEvents(False)
        
        self.setParentItem(self.parent)


    def afficher(self, valeur, valide):
        """lorsque cette case est sur le chemin d'un pion,
            un disque blanc ou rouge apparait indiquant le
            mode de deplacement (icone: 'dep_simple', 'nage', 'escalade', 'vol')
            un cout de 0 fait disparaitre ce symbole"""
        if valeur > 0:
            #couleur
            if valide:
                couleur = QColor(255, 255, 220, 200)
            else:
                couleur = QColor(240, 60, 20, 200)

            self.setBrush(couleur)
            self.setPen(QPen(couleur))

            #txt
            self.txt.setPlainText(QString.fromUtf8(str(valeur)))
            self.centrerTexte()

            self.setVisible(True)
        else:
            self.setVisible(False)

    def centrerTexte(self):
        f = QTextBlockFormat()
        f.setAlignment(Qt.AlignCenter)
        cursor = self.txt.textCursor()
        cursor.select(QTextCursor.Document)
        cursor.mergeBlockFormat(f)
        cursor.clearSelection()
        self.txt.setTextCursor(cursor)


            
            
            

