#include "state.h"
#include "move.h"
#include <iostream>
#include <cstdlib>

using namespace std;

State::State()
{}
State::~State()
{}

void State::affichage() {
    cout << endl << "------------------------------" << endl;
    cout << "Affichage du taquin actuel : " << endl;

    for(int i = 0; i < cases.size(); i++)
    {
        for(int j = 0; j < cases[i].size(); j++) {
            cout << cases[i][j] << "\t";
        }
        cout << endl;
    }
    cout << endl << "Cout du mouvement: " << cost << endl ;
    cout << "Heuristique : " << heuristique() << endl ;

    cout << endl << endl;
}

std::vector<int> State::findCell(int cell) const {
    std::vector<int> coordCell;

    std::vector<std::vector<int> > finalTaquin = getFinalTaquin();
    for(int i = 0; i < finalTaquin.size(); i++) {
        for(int j = 0; j < finalTaquin[i].size(); j++) {
            if(cell == finalTaquin[i][j]) {
                coordCell.push_back(i);
                coordCell.push_back(j);
                return coordCell;
            }
        }
    }
}

State State::move(int destination) {

    std::vector<std::vector<int> > copy(cases);
    int height = copy.size();
    int width = copy[0].size();
    int space_i, space_j;

    for(int i = 0; i < height; i++) {

        for(int j = 0; j < width; j++) {

            if(copy[i][j] == 0) {
                space_i = i;
                space_j = j;
            }
        }
    }

    switch(destination) {
        case DOWN:
            if(height > space_i - 1 && space_i - 1 >=0) {
                copy[space_i][space_j] = copy[space_i-1][space_j];
                copy[space_i-1][space_j] = 0;
            }
            break;
        case UP:
            if(height > space_i + 1 && space_i + 1 >=0) {
                copy[space_i][space_j] = copy[space_i + 1][space_j];
                copy[space_i + 1][space_j] = 0;
            }
            break;
        case LEFT:
            if(width > space_j+1 && space_j + 1 >= 0) {
                copy[space_i][space_j] = copy[space_i][space_j + 1];
                copy[space_i][space_j + 1] = 0;
            }
            break;
        case RIGHT:
            if(width > space_j - 1 && space_j - 1 >= 0) {
                copy[space_i][space_j] = copy[space_i][space_j - 1];
                copy[space_i][space_j - 1] = 0;
            }
            break;
        default:;
    }
    State s;
    s.cases = copy;
    return s;
}

std::vector<std::vector<int> > State::getFinalTaquin() const {
    std::vector<std::vector<int> > finalTaquin;

    // Désolé, on a pas réussi à développer cette partie là, malgré le reste, blocage intellectuel

    if(cases.size() == 3) {
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[0].push_back(1);
        finalTaquin[0].push_back(2);
        finalTaquin[0].push_back(3);
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[1].push_back(8);
        finalTaquin[1].push_back(0);
        finalTaquin[1].push_back(4);
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[2].push_back(7);
        finalTaquin[2].push_back(6);
        finalTaquin[2].push_back(5);
    }
    else if(cases.size() == 4) {
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[0].push_back(1);
        finalTaquin[0].push_back(2);
        finalTaquin[0].push_back(3);
        finalTaquin[0].push_back(4);
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[1].push_back(12);
        finalTaquin[1].push_back(13);
        finalTaquin[1].push_back(14);
        finalTaquin[1].push_back(5);
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[2].push_back(11);
        finalTaquin[2].push_back(16);
        finalTaquin[2].push_back(15);
        finalTaquin[2].push_back(6);
        finalTaquin.push_back(std::vector<int>());
        finalTaquin[3].push_back(10);
        finalTaquin[3].push_back(9);
        finalTaquin[3].push_back(8);
        finalTaquin[3].push_back(7);
    }
    else finalTaquin = cases;

    return finalTaquin;
}

bool State::equalsTo(State obj) {

    if(&obj == NULL) return false;

	for(int i=0; i < cases.size(); i++) {
		for(int j=0; j < cases[0].size(); j++) {

			if(cases[i][j] != obj.cases[i][j]) {
				return false;
			}
		}
	}

	return true;
}

int State::heuristique() const {
    return heuristique_man();
}

bool State::isFinal() const {

    std::vector<unsigned int> spiral;
    for (unsigned int l = 0; l < cases.size() / 2; l++)
    {
        int min = l;
        int max = cases.size() - 1 - l;

        for (int i = min; i < max; i++)
            spiral.push_back(cases[min][i]);

        for (int j = min; j < max; j++)
            spiral.push_back(cases[j][max]);

        for (int i = max; i > min; i--)
            spiral.push_back(cases[max][i]);

        for (int j = max; j > min; j--)
            spiral.push_back(cases[j][min]);
    }

    if (cases.size() % 2 == 1)
        spiral.push_back(cases[cases.size() / 2][cases.size() / 2]);

    for (unsigned int i = 0; i < spiral.size() - 1; ++i)
        if (spiral[i] != i + 1)
            return false;

    return true;
}

int State::heuristique_p() const {

    std::vector<unsigned int> spiral;

    for (unsigned int l = 0; l < cases.size() / 2; l++)
    {
        int min = l;
        int max = cases.size() - 1 - l;

        for (int i = min; i < max; i++)
            spiral.push_back(cases[min][i]);

        for (int j = min; j < max; j++)
            spiral.push_back(cases[j][max]);

        for (int i = max; i > min; i--)
            spiral.push_back(cases[max][i]);

        for (int j = max; j > min; j--)
            spiral.push_back(cases[j][min]);
    }

    if (cases.size() % 2 == 1)
        spiral.push_back(cases[cases.size() / 2][cases.size() / 2]);

    int error = 0;
    for (unsigned int i = 0; i < spiral.size() - 1; ++i)
        if (spiral[i] != i + 1)
            error++;

    return error;
}

int State::heuristique_man() const {

    int heurist = 0;
    int cpt = 0;

    std::vector<unsigned int> spiral;

    for (unsigned int l = 0; l < cases.size() / 2; l++)
    {
        int min = l;
        int max = cases.size() - 1 - l;

        for (int i = min; i < max; i++) {
            spiral.push_back(cases[min][i]);
            std::vector<int> tmp = findCell(cases[min][i]);
            heurist += abs(tmp[0] - min) + abs(tmp[1] - i);
            cpt++;
        }

        for (int j = min; j < max; j++) {
            spiral.push_back(cases[j][max]);
            std::vector<int> tmp = findCell(cases[j][max]);
            heurist += abs(tmp[0] - j) + abs(tmp[1] - max);
            cpt++;
        }

        for (int i = max; i > min; i--) {
            spiral.push_back(cases[max][i]);
            std::vector<int> tmp = findCell(cases[max][i]);
            heurist += abs(tmp[0] - max) + abs(tmp[1] - i);
            cpt++;
        }

        for (int j = max; j > min; j--) {
            spiral.push_back(cases[j][min]);
            std::vector<int> tmp = findCell(cases[j][min]);
            heurist += abs(tmp[0] - j) + abs(tmp[1] - min);
            cpt++;
        }
    }

    return heurist;
}
