# -*- coding: latin-1 -*-

import os
import SQL.Base

import Partida
import ControlPosicion
import Jugada
import Util

#==========================================================================================================================================
class MJugada :
    def __init__( self, tipo ) :
        self.tipo = tipo
        self.pieza=""
        self.desde=""
        self.hasta=""
        self.coronacion=""
        self.siJaque = False
        self.siMate = False
        self.siDesconocido = False
        self.siAbandono = False
        self.siFinalizada = False
        self.siCaptura = False
        self.enroque=""
        self.critica = ""
        self.comentario = ""

    def mueve( self, posicion ) :

        if self.tipo == "j" :
            posTmp = posicion.copia()
            self.desde = posTmp.determinaDesde( self.hasta, self.pieza, self.desde )
        else :
            dcol = "e"
            hcol = "c" if self.enroque == "O-O-O" else "g"
            dfil = hfil = "1" if posicion.siBlancas else "8"
            self.desde = dcol+dfil
            self.hasta = hcol+hfil
        try :
            sibien, mensaje, jg = Jugada.dameJugada( posicion, self.desde, self.hasta, self.coronacion )
            posicion = jg.posicion
            if self.siMate :
                jg.siJaqueMate = True
        except :
            sibien = False
            jg = None

        return sibien, posicion, jg

#==========================================================================================================================================
class UnPGN :
    def __init__( self, dic = None ) :
        self.reset( dic )

    #--------------------------------------------------------------------------------------------------
    def reset( self, dic = None ) :
        if dic is None :
            dic = {}
        self.dic = dic
        self.coronacion = ""
        self.texto = ""

        self.siError = False

    #--------------------------------------------------------------------------------------------------
    def leeCabecera( self, linea ) :
        linea = linea.strip()
        if linea.startswith( "[" ) and linea.endswith( "]" ) :
            li = linea[1:-1].split( '"' )
            if len(li) == 3 :
                clave = li[0].strip().upper()
                valor = li[1].strip()
                if clave and valor :
                    self.dic[clave] = valor

    #--------------------------------------------------------------------------------------------------
    def leeComentarioLinea( self, texto, nPos, nMax ) :
        coment = ""
        while nPos < nMax :
            c = texto[nPos]
            if c == "\n" :
                break
            coment += c
            nPos += 1
        return nPos, coment

    #--------------------------------------------------------------------------------------------------
    def leeComentario( self, texto, tipo, nPos, nMax ) :
        if tipo == ";" :
            return self.leeComentarioLinea( texto, nPos, nMax )

        contrario = ")" if tipo == "(" else "}"
        coment = ""
        nPendientes = 1
        while nPos < nMax :
            c = texto[nPos]
            if c == contrario :
                nPendientes -= 1
                if nPendientes == 0 :
                    nPos += 1
                    break
            elif c == tipo :
                nPendientes += 1
            coment += c
            nPos += 1
        return nPos, coment.replace( "\n", " " ).replace( "\r", " " ).strip()

    #--------------------------------------------------------------------------------------------------
    def leeEnroque( self, texto, nPos, nMax ) :
        enroque = ""
        siJaque = False
        while nPos < nMax :
            c = texto[nPos]
            if c in "oO-" :
                enroque += c
                nPos += 1
            elif c == "+" :
                siJaque = True
                nPos += 1

            else :
                break

        mjg = MJugada( "e" )
        mjg.enroque = enroque.upper()
        mjg.siJaque = siJaque

        return nPos, mjg

    #--------------------------------------------------------------------------------------------------
    def leeJugada( self, texto, nPos, nMax ) :
        pieza = "P"
        desde = ""
        hasta = ""
        coronacion = ""
        siJaque = False
        siMate = False
        siDesconocido = False
        siAbandono = False
        siCaptura = False
        while nPos < nMax :
            c = texto[nPos]
            if c in "abcdefgh" and (nPos+1)<nMax:
                c1 = texto[nPos+1]
                if c1.isdigit() :
                    if hasta :
                        desde = hasta
                    hasta = c+c1
                    nPos += 1
                elif c1 in ".p" : # en passant ep o e.p.
                    nPos += 1
                else :
                    desde = c
            elif c in "KQRBN" :
                if hasta :
                    coronacion = c
                else :
                    pieza = c
                    if (nPos+1)<nMax :
                        c1 = texto[nPos+1]
                        if c1.isdigit() :
                            desde = c1
                            nPos += 1
            elif c == "+" :
                siJaque = True

            elif c == "x" :
                siCaptura = True

            elif c.isdigit() or (c in "({Oo$?! \n\r") :
                break

            elif c == "#" :
                siMate = True

            elif c == "*" :
                siDesconocido = True

            nPos += 1

        mjg = MJugada( "j" )
        mjg.pieza = pieza
        mjg.desde = desde
        mjg.hasta = hasta
        mjg.coronacion = coronacion
        mjg.siJaque = siJaque
        mjg.siMate = siMate
        mjg.siDesconocido = siDesconocido
        mjg.siAbandono = siAbandono
        mjg.siCaptura = siCaptura

        return nPos, mjg

    #--------------------------------------------------------------------------------------------------
    def leeCritica( self, texto, nPos, nMax ) :
        num = ""
        nPos += 1
        while nPos < nMax :
            c = texto[nPos]
            if c.isdigit():
                num += c
            else :
                break
            nPos += 1
        return nPos, num

    #--------------------------------------------------------------------------------------------------
    def leePartida( self ) :
        texto = self.texto

        nPos = 0
        nMax = len(texto)
        jg = None
        posicion = ControlPosicion.ControlPosicion()
        if "FEN" in self.dic :
            posicion.leeFen( self.dic["FEN"] )
        else :
            posicion.posInicial()
        self.partida = Partida.Partida(posicion)

        while nPos < nMax :
            c = texto[nPos]

            if c.isdigit() :
                nPos += 1
                while nPos < nMax and texto[nPos] in "0123456789./-" :
                    nPos += 1

            elif c in "({;" :
                nPos, comentario = self.leeComentario( texto, c, nPos+1, nMax )
                if jg :
                    if c in "{;" :
                        if jg.comentario :
                            jg.comentario += "\n"
                        jg.comentario += comentario
                    elif c == "(" :
                        if jg.variantes :
                            jg.variantes += "\n\n"
                        jg.variantes += comentario

            elif c in "!?" :
                if jg :
                    jg.comentario += c

            elif c in "$" :
                nPos, critica = self.leeCritica( texto, nPos, nMax )
                if jg :
                    if critica.isdigit() :
                        i = int(critica)
                        if i > 0 and i < 140 :
                            jg.critica = critica

            elif c in " " :
                nPos += 1

            elif c in "KQRBNabcdefgh" :
                nPos, mjg = self.leeJugada( texto, nPos, nMax )
                sibien, posicion, jg = mjg.mueve( posicion )
                if sibien :
                    self.partida.liJugadas.append( jg )
                    jg.comentario = ""
                    jg.critica = ""
                else :
                    self.siError = True
                    break

            elif c in "Oo" :
                nPos, mjg = self.leeEnroque( texto, nPos, nMax )
                sibien, posicion, jg = mjg.mueve( posicion )
                if sibien :
                    self.partida.liJugadas.append( jg )
                    jg.comentario = ""
                    jg.critica = ""
                else :
                    self.siError = True
                    break

            else :
                nPos += 1

        if (not self.siError) and self.partida.liJugadas :
            self.partida.ultPosicion = jg.posicion

    #------------------------------------------------------------------------------------------------------------------------
    def leeTexto( self, texto ) :
        self.reset()

        li = texto.split( "\n" )
        jugadas = ""
        for linea in li :
            linea = linea
            if linea :
                if linea.startswith( "[" ) :
                    self.leeCabecera( linea )
                else :
                    jugadas += "\n" + linea
        self.texto = jugadas
        self.leePartida()
        self.siError |= len(self.partida.liJugadas) == 0

    #------------------------------------------------------------------------------------------------------------------------
    def variable( self, clave, defecto = "" ) :
        if clave in self.dic :
            return self.dic[clave]
        else :
            return defecto

#==========================================================================================================================================
class PGN :

    #------------------------------------------------------------------------------------------------------------------------
    def __init__( self ) :
        self.cdir = "dataDB"
        self.cpkd = self.cdir + "/dataDB.pkd"
        self.maxdb = 10
        self.leePKD()

    #------------------------------------------------------------------------------------------------------------------------
    def leePKD( self ) :
        # Comprueba si existe la carpeta dataDB, sino la crea
        if not os.path.isdir( self.cdir ) :
            os.mkdir( self.cdir )

        # Comprueba si existe en dataDB - dataDB.pk
        if os.path.isfile( self.cpkd ) :
            self.liDatos = Util.recuperaDIC( self.cpkd )
        else :
            self.liDatos = []

    #------------------------------------------------------------------------------------------------------------------------
    def damePosicion( self, fichero ) :
        for n, uno in enumerate(self.liDatos) :
            if uno["FICHERO"] == fichero :
                return n
        if len(self.liDatos) >= self.maxdb :
            nPrimero = -1
            fecha = "99999"
            for n, uno in enumerate(self.liDatos) :
                try :
                    if uno["TAM"] == 0 or uno["FICHERO"] == "" or "FECHA" not in uno :
                        return n
                    if uno["FECHA"] < fecha :
                        fecha = uno["FECHA"]
                        nPrimero = n
                except :
                    return n
            return nPrimero

        # nuevo
        uno = {}
        uno["FICHERO"] = ""
        self.liDatos.append(uno)
        return len(self.liDatos)-1

    #------------------------------------------------------------------------------------------------------------------------
    def borraReferenciaA( self, fichero ) :
        n = self.damePosicion( fichero )
        self.liDatos[n]["TAM"] = 0
        Util.guardaDIC( self.liDatos, self.cpkd )

    #------------------------------------------------------------------------------------------------------------------------
    def compruebaPosicion( self, fichero, uno ) :

        # Mismo fichero
        if uno["FICHERO"] != fichero :
            return False

        # Mismo tamaño
        tam = Util.tamFichero( uno["FICHERO"] )
        if tam != uno["TAM"] :
            return False

        # Misma fecha
        fecha = os.path.getmtime( uno["FICHERO"] )
        if fecha != uno["FECHA"] :
            return False

        # Que exista el DB
        if not os.path.isfile( uno["PATHDB"] ) :
            return False

        return True

    #------------------------------------------------------------------------------------------------------------------------
    def creaDB( self, fichero, uno, rutina ) :

        # Rutina( proceso, 0..99 )
        tamR = Util.tamFichero( fichero )/100.0

        # Leemos la lista de PGNs,
        # Para crear el db necesitamos saber la lista de campos, entonces, primero leemos una vez, determinamos la lista y luego volvemos a leer y a la vez grabamos
        dClaves = {}
        f = open( fichero, "rb" )

        nR = actR = 0
        procesoR = 1

        nPosClave = 0

        for linea in f :

            nR += len(linea)
            if nR/tamR > actR :
                actR += 1
                if not rutina( procesoR, actR ) :
                    return False

            if linea.startswith( "[" ) :
                li = linea.split( '"' )
                if len( li ) == 3 :
                    clave = li[0].replace("[", "").strip().upper()
                    tam = len(li[1])
                    if clave in dClaves :
                        if dClaves[clave][0] < tam :
                            dClaves[clave][0] = tam
                    else :
                        dClaves[clave] = [tam, nPosClave]
                        nPosClave += 1
        uno["DICCLAVES"] = dClaves
        # Creamos la base de datos con la información sobre las claves y tamaño
        fichDB = uno["PATHDB"]
        try :
            os.remove( fichDB )
        except :
            pass
        bd = SQL.Base.DBBase( fichDB )

        tb = SQL.Base.TablaBase()
        tb.nombre = "JUEGOS"
        tb.liCampos = liCampos = []
        for clave,v in dClaves.iteritems() :
            tam, pos = v
            liCampos.append( SQL.Base.Campo( clave, 'VARCHAR(%d)'%tam ) )
        liCampos.append( SQL.Base.Campo( "JUGADAS", 'TEXT' ) )
        cursor = bd.conexion.cursor()
        tb.crearBase( cursor )

        # Hacemos la lectura y grabamos los registros
        dbf = bd.dbf( "JUEGOS", ",".join(dClaves.keys())+",JUGADAS" )

        unPgn = UnPGN()

        f.seek( 0 ) # Nos ponemos al principio de nuevo

        jugadas = ""
        jg = 0

        nR = actR = 0
        procesoR = 2

        for linea in f :

            nR += len(linea)
            if nR/tamR > actR :
                actR += 1
                if not rutina( procesoR, actR ) :
                    return False

            linea = linea
            if linea.startswith( "[" ) :
                if jugadas :

                    unPgn.dic["JUGADAS"] = jugadas
                    dbf.soloGrabar( unPgn.dic, jg%10000==0 ) # Sólo hacemos commit cada 10000 registros
                    jg += 1
                    unPgn.reset()
                    jugadas = ""
                unPgn.leeCabecera( linea )
            else :
                if linea :
                    jugadas += "\n" + linea
        if jugadas :
            unPgn.dic["JUGADAS"] = jugadas
            dbf.soloGrabar( unPgn.dic, False )
        dbf.commit() # Para una escritura rápida

        bd.cerrar()
        f.close()
        rutina( 3, 0 )

        return True

    #------------------------------------------------------------------------------------------------------------------------
    def leeFichero( self, fichero, rutina ) :

        fichero = os.path.abspath(fichero)

        # Pedimos la posición que le corresponde
        nPos = self.damePosicion( fichero )

        uno = self.liDatos[nPos]

        if not self.compruebaPosicion( fichero, uno ) :
            uno["FICHERO"] = fichero
            uno["TAM"] = Util.tamFichero( fichero )
            uno["FECHA"] = os.path.getmtime( uno["FICHERO"] )
            uno["PATHDB"] = self.cdir + "/pgnDB%03d.db3"%nPos
            if self.creaDB( fichero, uno, rutina ) :
                Util.guardaDIC( self.liDatos, self.cpkd )
            else :
                return None

        return uno

#==========================================================================================================================================
