#include "gamemanager.h"
#include "random"
#include <QFileInfo>
#include <QDebug>

GameManager::GameManager(QObject *parent) : QObject(parent), grid(4, std::vector<int>(4, 0)),m_score(0), m_gameOver(false) {

}

void GameManager::moveLeft() {
    if (m_gameOver) {  // Si la partie est terminée, ne fais rien
        return;
    }
    for (int i = 0; i < 4; i++) {
        std::vector<int> newRow(4, 0);  // Créer une nouvelle ligne vide
        int newPos = 0;  // Position pour insérer un élément non nul (commence par le début)
        int temp = 0;  // Variable pour mémoriser l'élément à fusionner

        // Parcourir la ligne de gauche à droite
        for (int j = 0; j <4; j++) {
            if (grid[i][j] != 0) {  // Si l'élément n'est pas nul
                if (temp == 0) {
                    // Mémoriser l'élément actuel
                    temp = grid[i][j];
                } else if (temp == grid[i][j]) {
                    // Si l'élément est identique au "temp", fusionner
                    newRow[newPos] = temp * 2;  // Fusionner en doublant la valeur
                    temp = 0;  // Réinitialiser temp après fusion
                    newPos++;  // Déplacer la position pour l'élément suivant
                } else {
                    // Si l'élément n'est pas identique à "temp", le placer dans la nouvelle ligne
                    newRow[newPos] = temp;
                    temp = grid[i][j];  // Mettre à jour temp avec le nouvel élément
                    newPos++;  // Déplacer la position pour l'élément suivant
                }
            }
        }

        // Si l'élément restant dans "temp" n'a pas été placé, on le place
        if (temp != 0) {
            newRow[newPos] = temp;
        }

        // Mettre à jour la grille avec la nouvelle ligne
        grid[i] = newRow;
    }
    emit addRandomElement();
    m_gameOver = isGameOver();
    //Mettre a jour l'état et l'enregistrer
    historyArray.append(gridToJsonArray());
    emit gridChanged();
    emit calculscore();
}

void GameManager::moveRight() {
    if (m_gameOver) {  // Si la partie est terminée, ne fais rien
        return;
    }
    for (int i = 0; i < 4; i++) {
        std::vector<int> newRow(4, 0);  // Créer une nouvelle ligne vide
        int newPos = 3;  // Position pour insérer un élément non nul (commence par la fin)
        int temp = 0;  // Variable pour mémoriser l'élément à fusionner

        // Parcourir la ligne de droite à gauche
        for (int j = 3; j >= 0; j--) {
            if (grid[i][j] != 0) {  // Si l'élément n'est pas nul
                if (temp == 0) {
                    // Mémoriser l'élément actuel
                    temp = grid[i][j];
                } else if (temp == grid[i][j]) {
                    // Si l'élément est identique au "temp", fusionner
                    newRow[newPos] = temp * 2;  // Fusionner en doublant la valeur
                    temp = 0;  // Réinitialiser temp après fusion
                    newPos--;  // Déplacer la position pour l'élément suivant
                } else {
                    // Si l'élément n'est pas identique à "temp", le placer dans la nouvelle ligne
                    newRow[newPos] = temp;
                    temp = grid[i][j];  // Mettre à jour temp avec le nouvel élément
                    newPos--;  // Déplacer la position pour l'élément suivant
                }
            }
        }

        // Si l'élément restant dans "temp" n'a pas été placé, on le place
        if (temp != 0) {
            newRow[newPos] = temp;
        }

        // Mettre à jour la grille avec la nouvelle ligne
        grid[i] = newRow;
    }
    emit addRandomElement();
    m_gameOver = isGameOver();

    //Mettre a jour l'état et l'enregistrer
    historyArray.append(gridToJsonArray());
    emit gridChanged();
    emit calculscore();

}



void GameManager::moveUp() {
    if (m_gameOver) {  // Si la partie est terminée, ne fais rien
        return;
    }
    // Implémentation du mouvement vers le haut
    for (int j = 0; j < 4; j++) {
        std::vector<int> newCol(4, 0);  // Créer une nouvelle ligne vide
        int newPos = 0;  // Position pour insérer un élément non nul (commence par la fin)
        int temp = 0;  // Variable pour mémoriser l'élément à fusionner

        // Parcourir la ligne de droite à gauche
        for (int i = 0; i <4; i++) {
            if (grid[i][j] != 0) {  // Si l'élément n'est pas nul
                if (temp == 0) {
                    // Mémoriser l'élément actuel
                    temp = grid[i][j];
                } else if (temp == grid[i][j]) {
                    // Si l'élément est identique au "temp", fusionner
                    newCol[newPos] = temp * 2;  // Fusionner en doublant la valeur
                    temp = 0;  // Réinitialiser temp après fusion
                    newPos++;  // Déplacer la position pour l'élément suivant
                } else {
                    // Si l'élément n'est pas identique à "temp", le placer dans la nouvelle ligne
                    newCol[newPos] = temp;
                    temp = grid[i][j];  // Mettre à jour temp avec le nouvel élément
                    newPos++;  // Déplacer la position pour l'élément suivant
                }
            }
        }

        // Si l'élément restant dans "temp" n'a pas été placé, on le place
        if (temp != 0) {
            newCol[newPos] = temp;
        }

        // Mettre à jour la colonne dans la grille
        for (int i=0;i<4;i++){
            grid[i][j]=newCol[i];
        }

    }
    emit addRandomElement();
    m_gameOver = isGameOver();

    //Mettre a jour l'état et l'enregistrer
    historyArray.append(gridToJsonArray());
    emit gridChanged();
    emit calculscore();
}

void GameManager::moveDown() {
    if (m_gameOver) {  // Si la partie est terminée, ne fais rien
        return;
    }
    // Implémentation du mouvement vers le bas
    for (int j = 0; j < 4; j++) {
        std::vector<int> newCol(4, 0);  // Créer une nouvelle ligne vide
        int newPos = 3;  // Position pour insérer un élément non nul (commence par la fin)
        int temp = 0;  // Variable pour mémoriser l'élément à fusionner

        // Parcourir la ligne de droite à gauche
        for (int i = 3; i>=0; i--) {
            if (grid[i][j] != 0) {  // Si l'élément n'est pas nul
                if (temp == 0) {
                    // Mémoriser l'élément actuel
                    temp = grid[i][j];
                } else if (temp == grid[i][j]) {
                    // Si l'élément est identique au "temp", fusionner
                    newCol[newPos] = temp * 2;  // Fusionner en doublant la valeur
                    temp = 0;  // Réinitialiser temp après fusion
                    newPos--;  // Déplacer la position pour l'élément suivant
                } else {
                    // Si l'élément n'est pas identique à "temp", le placer dans la nouvelle ligne
                    newCol[newPos] = temp;
                    temp = grid[i][j];  // Mettre à jour temp avec le nouvel élément
                    newPos--;  // Déplacer la position pour l'élément suivant
                }
            }
        }

        // Si l'élément restant dans "temp" n'a pas été placé, on le place
        if (temp != 0) {
            newCol[newPos] = temp;
        }

        // Mettre à jour la colonne dans la grille
        for (int i=0;i<4;i++){
            grid[i][j]=newCol[i];
        }

    }
    emit addRandomElement();
    m_gameOver = isGameOver();

    //Mettre a jour l'état et l'enregistrer
    historyArray.append(gridToJsonArray());
    emit gridChanged();
    emit calculscore();

}

void GameManager::restartGame(QString partieName) {

    //demander pour sauvergarder la partie précédente

<<<<<<< HEAD
    if(partieName=="false"){
        }else{
        enregistrerPartie(partieName);
        };
=======
    enregistrerPartie("partie1");
    m_gameOver = false;
>>>>>>> e22daca7a3c09bd0a515d12093b0cbf7e3e4e152


    // Réinitialisation des variables
    grid.assign(4, std::vector<int>(4, 0));
    historyArray = QJsonArray();

    std::random_device rd;
    std::uniform_int_distribution<int> distrib(0, 15);

    int num1 = distrib(rd);
    int num2;

    // Assure que num2 est différent de num1
    do {
        num2 = distrib(rd);
    } while (num2 == num1);

    int rg1= num1/4;
    int col1= num1 - (4*rg1);

    int rg2= num2/4;
    int col2= num2 - (4*rg2);

    grid[rg1][col1]=2;
    grid[rg2][col2]=2;


    //Mettre a jour l'état et l'enregistrer
    historyArray.append(gridToJsonArray());
    emit gridChanged();
    emit calculscore();

}

void GameManager::addRandomElement() {
    // Créer une liste pour stocker les indices des cases vides
    std::vector<std::pair<int, int>> emptyCells;

    // Parcourir la grille pour trouver toutes les cases vides
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (grid[i][j] == 0) {  // Si la case est vide (valeur 0)
                emptyCells.push_back({i, j});  // Ajouter l'indice de la case à la liste
            }
        }
    }

    // Si la liste des cases vides n'est pas vide, on ajoute un nouveau '2'
    if (!emptyCells.empty()) {
        // Choisir une case vide aléatoire
        int randomIndex = rand() % emptyCells.size();
        int row = emptyCells[randomIndex].first;
        int col = emptyCells[randomIndex].second;

        // Placer un '2'ou un '4' dans cette case
        int randValue = std::rand() % 100;
        if (randValue < 75) {
            grid[row][col] = 2;
        } else {
            grid[row][col] = 4;
        };
    }
}

QVector<int> GameManager::getGridValues() const {
    QVector<int> flattenedGrid;
    for (const auto& row : grid) {  // Parcourt chaque ligne
        for (int value : row) {      // Parcourt chaque colonne
            flattenedGrid.append(value);
        }
    }
    return flattenedGrid;
}

QStringList GameManager::getPartieHistorique() {
    QFile file("historique.json");

    qDebug() << "Fichier JSON enregistré à : " << QFileInfo(file).absoluteFilePath();

    if (!file.open(QIODevice::ReadWrite)) {
        qWarning() << "Impossible d'ouvrir le fichier pour écriture :" << file.errorString();

    }
    QJsonDocument doc;
    if (file.size() > 0) {  // Vérifier que le fichier n'est pas vide
        doc = QJsonDocument::fromJson(file.readAll());
    }else{  // Sinon initialiser avec un objet vide
        doc = QJsonDocument(QJsonObject());
    }


    QJsonObject rootObj = doc.object();
    QStringList list = rootObj.keys();

    file.close();

    qDebug() << "Liste des parties:" << list ;
    return list;

}

QJsonArray GameManager::gridToJsonArray() {
    QJsonArray jsonGrid;
    for (const auto &row : grid) {
        QJsonArray jsonRow;
        for (int cell : row) {
            jsonRow.append(cell);
        }
        jsonGrid.append(jsonRow); // Ajoute chaque ligne
    }
    return jsonGrid;
}

void GameManager::undo() {
    if (historyArray.size() > 1) {  // Vérifiez qu'il y a au moins deux états
        historyArray.removeLast();  // Supprimez le dernier état
        QJsonArray previousState = historyArray.last().toArray();  // Récupérez l'avant-dernier état

        // Mettez à jour la grille avec l'état précédent
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                grid[i][j] = previousState[i].toArray()[j].toInt();
            }
        }

        emit gridChanged();  // Notifiez que la grille a changé
        emit calculscore();
    }
}

void GameManager::enregistrerPartie(QString partieName){
    QFile file("historique.json");
    qDebug() << "Fichier JSON enregistré à : " << QFileInfo(file).absoluteFilePath();
    // Ouvrir le fichier en mode lecture/écriture (si existant)
    if (!file.open(QIODevice::ReadWrite)) {
        qWarning() << "Impossible d'ouvrir le fichier pour écriture :" << file.errorString();
        return;
    }


    QJsonDocument doc;
    if (file.size() > 0) {  // Vérifier que le fichier n'est pas vide
        doc = QJsonDocument::fromJson(file.readAll());
    }else{  // Sinon initialiser avec un objet vide
        doc = QJsonDocument(QJsonObject());
    }


    QJsonObject rootObj = doc.object(); // Récupérer l'objet JSON racine
    rootObj[partieName] = historyArray; // Ajouter ou remplacer la grille associée à l'ID

    // Réécriture du fichier avec la nouvelle version
    file.resize(0); // Effacer le contenu du fichier avant d'écrire la nouvelle version
    file.write(QJsonDocument(rootObj).toJson());
    file.close();

    emit historiqueChanged();
}

void GameManager::chargerPartie(QString partieName){
    QFile file("historique.json");


    // Ouvrir le fichier en mode lecture/écriture (si existant)
    if (!file.open(QIODevice::ReadWrite)) {
        qWarning() << "Impossible d'ouvrir le fichier pour écriture :" << file.errorString();
        return;
    }


    QJsonDocument doc;
    if (file.size() > 0) {  // Vérifier que le fichier n'est pas vide
        doc = QJsonDocument::fromJson(file.readAll());
    }else{  // Sinon erreur
        return;
    }

    QJsonObject rootObj = doc.object(); // Récupérer l'objet JSON racine
    historyArray = rootObj[partieName].toArray();  // Charger la grille

    QJsonArray previousState = historyArray.last().toArray();  // Récupérez le dernier état de la partie chargée

    // Mettez à jour la grille
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            grid[i][j] = previousState[i].toArray()[j].toInt();
        }
    }

    emit gridChanged();  // Notifiez que la grille a changé


    file.close();
}

int GameManager::score() const {
    return m_score;  // Retourne le score actuel
}

<<<<<<< HEAD


=======
void GameManager::calculscore(){
    if (m_gameOver) {  // Si la partie est terminée, ne calcule pas le score
        return;
    }
    m_score = 0;
    // Parcourir la grille pour additionner toute les cases
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            m_score+=grid[i][j];

        }
    }
    emit scoreChanged();
}

bool GameManager::isGameOver() {

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (grid[i][j] == 0) {  // Une case vide est trouvée, donc le jeu n'est pas fini
                return false;
            }
            // Vérifier les possibilités de fusion (horizontale et verticale)
            if ((i < 3 && grid[i][j] == grid[i + 1][j]) || (j < 3 && grid[i][j] == grid[i][j + 1])) {
                return false;
            }
        }
    }
    return true;  // Si la grille est pleine et aucune fusion n'est possible, la partie est terminée
}
>>>>>>> e22daca7a3c09bd0a515d12093b0cbf7e3e4e152