/***********************************************************************
 *
 * Copyright (C) 2010-2017 Innocent De Marchi <tangram.peces@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ***********************************************************************/
#ifndef TAULER_H
#define TAULER_H

#include <QtWidgets/QGraphicsView>
#include <QtWidgets/QUndoStack>
#include <QtWidgets/QProgressDialog>
#include <QHash>
#include <QtGui>
#include "constants.h"

class frmPrincipal;
class Rellotge;
class Fitxa;

class Tauler: public QGraphicsView
{
    Q_GADGET
public:
    Tauler( QUndoStack  *movimentsUndoStack, Rellotge *rellotge,frmPrincipal *frmPrinci, QWidget *parent = 0);

    //QGraphicsScene* sceneJoc;

    void joc_llegeixArxiuData();
    void joc_CarregaJoc( QString p_tipusJoc, QString p_filesColumnes, QString p_estatFitxes);

    int estatFitxaJoc(const  QPoint& coordenades) const ;

    void marcaMovimentsFitxa(QList<QPoint> movimentsFitxa);
    void eliminaMarquesMoviments(const  QPoint& coordenades);
    void ferMoviment(int fitxaInicial, int fitxaBotada, int fitxaFinal, bool missatge=true);
    bool ferMoviment2(int fitxaInicial, int fitxaBotada, int fitxaFinal, bool missatge=true);
    void marcaFinalDeJoc(int direccioMoviment=1, bool marcaFinal=false, bool aturaRellotge=false);

    //Canvia l'estat de les fitxes per tal de marcar un moviment
    void marcaMoviment (QString moviment);


    QMap<int, Fitxa*> m_fitxes;
    //QHash<int, Fitxa*> m_fitxes;

    QUndoStack  *p_movimentsUndoStack;

    Rellotge *p_rellotge;


    //número total de fitxes actives del joc
    int comptadorFitxesJoc;
    //número total de fitxes amb estat 0 del joc
    int comptadorFitxesEstatZeroJoc;
    //número total de fitxes marcades
    //com a final de joc
    int comptadorFitxesFinalJoc;

    /* Comprova si el joc te el final marcat*/
    int jocAmbFinalMarcat();
    /* Dels moviments passats, selecciona els que tenen una fitxa marcada com a final
     * de joc com a final de moviment*/
    QStringList solucionaJocActual_seleccionarMovimentsAmbFitxesFinalsDeJoc(QStringList moviments);

    void eliminaFitxesVermelles();
    /* Posa totes les fitxes actives
     * a color blau
     */
    void eliminaFitxesVerdes();

    void solucionaJocActual_CalculaMovimentsPosibles();

//    QStringList solucionaJocActual_CarregaMovimentsArbre3
//       (int numeroInicialFitxesJoc, variables_recerca_solucio variablesSolucio);
//    QStringList solucionaJocActual_CarregaMovimentsArbre2
//                         (int numeroInicialFitxesJoc,
//                          variables_recerca_solucio variablesSolucio);
    QStringList solucionaJocActual_CarregaMovimentsArbre1
                        (QStringList movimentsFets, QString darrerMoviment);

    QStringList solucionaJocActual_CarregaMovimentsArbre4(int numeroInicialFitxesJoc);
    void solucionaJocActual_CalculaArbreJoc(QProgressDialog *progres, int numFitxesInicialsJoc, int mantenirMoviment);
    void solucionaJocActual_CalculaArbreJocIteracio(QProgressDialog *progres, int numFitxesInicialsJoc,
                                                    int mantenirMoviment=0);
    QStringList solucionaJocActual_CalculaArbreJoc_CalculaMoviments();

    void solucionaJocActual_CalculaArbreJocComplet_Inici(QProgressDialog *progres,
                                                           int numFitxesInicialsJoc);
    QStringList solucionaJocActual_CalculaArbreJocComplet_Inici_CalculaMoviments();
    void solucionaJocActual_CalculaArbreJocComplet_Selectiu(QProgressDialog *progres,
                                                           int numFitxesInicialsJoc, int mantenirMoviment);
    QStringList solucionaJocActual_CalculaArbreJocComplet_CalculaMoviments();
    void solucionaJocActual_CalculaArbreJoc_escriuMoviments();

    QStringList solucionaJocActual_CalculaNumeroMovimentsPosteriors(QStringList movimentsPosibles);
    QStringList solucionaJocActual_SeleccionaNumeroMovimentsPosteriors(QStringList llistaMovimentsMaxims,
                                                                      QList<int> llistaNumeroMoviments);
    /* Dels moviments possibles, selecciona aquells que redueixen el nombre de
     * conjunts de fitxes */
    QStringList solucionaJocActual_SeleccionaMovimentsRedueixenConjuntsFitxes(QStringList movimentsPosibles);
    /* Comprova si, desprès de fer el moviment passat per paràmetre,
     * és possible fer altres moviments
     * 20/11/12 S'ha eliminat: no és eficaç

    bool solucionaJocActual_CalculaMovimentsPosteriors(QString moviment);
    */
    QStringList solucionaJocActual_RedueixLlistaDeMoviments(QStringList movimentsPosibles);
    int solucionaJocActual_MovimentsPosiblesActuals();

    /*
      Comprova si hi ha alguna fitxa "aïllada"
      o sigui fitxes en estat 1 que no tenen cap moviment
      que es pugui fer efectiu o be cap moviment en què
      la fitxa mitjancera pugui passar a estat 1 desprès d'un moviment
      */
    //bool solucionaJocActual_FitxesAillades(); eliminat a la versió 2.1
    bool solucionaJocActual_FitxesAillades1();
    bool solucionaJocActual_FitxesAillades2();
    int solucionaJocActual_numeroFitxesAillades();
    int solucionaJocActual_FitxesAillades_PerConjunts();
    int solucionaJocActual_conjuntsDispersos(int numeroConjuntsActual);
    int solucionaJocActual_conjuntsAillats(int numeroConjuntsActual);
    QPoint distanciaDeUnaFitxaAAltres(QPoint coordenades, int proximitat=2);
    void fitxesProximesAUnAltra(QPoint coordenadesFitxaOriginal, int numConjuntAillament, int numContador);
    void fitxaDistanciaAillament(QPoint coordenadesFitxaOriginal, Fitxa *fitxaOriginal);
    /* Calcula la distància entre dues fitxes a partir de les seves coordenades*/
    int distanciaEntreDuesFitxes(QPoint coordenades1, QPoint coordenades2);
    bool movimentPosibleFitxaFinal(int fitxaMijana);
    bool movimentPosibleFitxaFinal1(int fitxaMijana);
    bool movimentPosibleFitxaMitjana(int fitxaMitjana);
    //No es fa servir
  //  bool movimentFitxaCentral();

    QString configuracioActual(bool comptarFitxes=false);
    /*10/02/13 AL final no es fa servir
    QString configuracioInicialAmbFitxesMarcades();*/

    //Carrega la configuració passada
    void carregaConfiguracio(QString configuracio);

    //Canvia l'estat de les fitxes a l'atzar
    //Es fa servir quan el joc està en pausa
    //No es fa servir pel tema de la sensibilitat al color
//    void estatFitxesAtzar();

    int numeroDeMovimentsJoc();

    //No es fa servir
   // QString missatgeNumeroMoviments();


    void setModalitatJocActual(QString valor);
    QString modalitatJocActual()const{
        return p_modalitatJocActual;}

    void setEstatDelJoc(EstatsDelJoc valor);

    EstatsDelJoc estatDelJoc() const{
        return p_estatDelJoc;}


    /* Conserva el codi (per la traducció)
     * del joc actual
     */
    QString p_codiNomJocActual;
    QString nomJocActual() const{
        return p_codiNomJocActual;
    }
    void setCodiNomJocActual (QString codi);

    //Numero de fitxes aïllades permeses per a cada modalitat
    int p_numFitxesAillades;

    //Número de moviments a "conservar" en cercar una  nova solució
    //a partir d'una altra
    int p_numMoviments;

    /* Tipus de moviment del joc actual
     *  1.directe 2.invers 3. Diagonal 5.Invers en diagonal?
     */
    int p_tipusMoviment;

    /*
     * En els jocs amb fitxes finals marcades,
     * controla si el número de moviments per acabar
     * el joc permet deixar les fitxes finals ocupades
     */
    bool ocuparFitxesFinalJoc();
    /* Retorna el número de fitxes marcades com a final
     * del joc que estan sense ocupar
     */
    int numeroFitxesFinalsSenseOcupar();
    int numeroFitxesPerEliminar();

    /* En la generació d'un joc a l'atzar
     * marca les fitxes que s'ha fet servir per
     * generar el joc
     */
    void solitariAtzar_MarcaFitxaUsada(int fitxa, bool marcaFitxaFinal=false);
    /* En la generació d'un joc a l'atzar
     * retorna l'estat de les fitxes del
     * joc generat a l'atzar
     */
    QString solitariAtzar_dadesEstatFitxes(int files, int columnes);

    /* Retorna les files i columnes, i les dades de les
     * fitxes d'un solitari personalitzat
     */
    QStringList solitariPersonalitzat_dadesFitxes();
    /* Retorna el número de files i columnes del joc
     * personalitzat
     */
    QString solitariPersonalitzat_filesColumnes();

    /* Marca els solitaris especials
     */
    bool p_SolitariAtzar;
    bool p_SolitariPersonalitzat;
    void setSolitariAtzar(bool valor);
    void setSolitariPersonalitzat(bool valor);

    /* Estableix si s'està jugant amb
     * un solitari modificat
     */
    void setSolitariModificat(bool valor);
    bool solitariModificat() const{
       return p_SolitariModificat;
    }

    bool solitariAtzar() const{
        return p_SolitariAtzar;}
    bool solitariPersonalitzat() const{
        return p_SolitariPersonalitzat;}
    //per comprova si es tracta d'algun solitari
    //especial
    bool esSolitariEspecial() const{
        return p_SolitariAtzar || p_SolitariPersonalitzat;
    }
    //Controla els tipus de joc especials
    void setSolitariEspecial(QString codiJoc);

    /* Marcam totes les fitxes com a personalitzades
     * (segons el valor passat)
     */
    void marcaFitxesSolitariPersonalitzat(bool valor);
    /* Comprova si hi ha alguna fitxa del solitari marcada
     * com a personalitzada
     * 10/02/13 Finalment no es fa servir això

    bool solitariPersonalitzat_hiHaFitxesMarcades();*/

    frmPrincipal *p_frmPrinci;

    bool jocFinalitzat();

    int jocsRealitzats=0;
    int jocsAmbSolucio=0;
    int maximNombreDeProves=300;
    bool continuar=true;

 protected:
    virtual void resizeEvent(QResizeEvent* event);

 private:

    /* Codi de la modalitat de solitari
     * actual
     */
    QString p_modalitatJocActual;
    int p_estatFitxaMitjana;

    bool nomesQuedenFitxesMarcadesFinalJoc();
    /* Comprova si s'ha arribat al final del joc. Això
     * és molt útil en les modalitats que tenen el final
     * de joc marcat
     */
   bool arribatFinalJoc();

    void controlaRecords();

    EstatsDelJoc p_estatDelJoc;

    /*Controla si el solitari és una modificació
     */
    bool p_SolitariModificat;

    /* Procediments de les modalitats a l'atzar
     */
   //Procediment inicial
   void solitariAtzar_generaJoc();
};

#endif // TAULER_H
