//
// Created by sasha on 28.11.2025.
//
#include "board.h"

#include <random>


Board::Board(IWidget *parent, float x, float y) : IWidget (parent,x,y,308,304) {
    background = new Image(this,x,y,308,304,"table_green.png");
}

Board::~Board() {

}


bool Board::createBoard(const std::string &type) {
    this->type = type;

    board_size=3;
    board_size_cube = 92;
    board_spacing = 6;

    if (type == "easy") {
        background->setTexture("table_green.png");
    }
    if (type == "normal") {
        background->setTexture("table_blue.png");
        board_size=4;
        board_size_cube = 67;
        board_spacing = 5;
    }
    if (type == "hard") {
        background->setTexture("table_red.png");
        board_size=5;
        board_size_cube = 52;
        board_spacing = 7;
    }


    // TODO: оптимизировать на нужно ли удалять, доиспользовать старые
    for (auto *cube : listCubes){
        delete cube;
    }
    listCubes.clear();


    for (int i=0;i<(board_size*board_size)-1;i++){
        int y = int(i/board_size);
        int x = i-y*board_size;

        float cube_x = 10+this->x+(x*(board_size_cube+board_spacing));
        float cube_y = 10+this->y+(y*(board_size_cube+board_spacing));

        auto cube = new Cube(this,cube_x,cube_y,type,i+1);
        cube->setName("cube_"+std::to_string(i+1));
        cube->setBoardPosition(x,y);
        cube->setCallback(CallbackType::EVENT_ON_CLICK,[this,cube]{
            onClickCube(cube);
        });
        listCubes.push_back(cube);

    }

    intermix();

    return true;
}

void Board::onClickCube(Cube *cube) {
    SDL_Log("onClickCube: %i",cube->getNumber());

    if (!cubeMove(cube)) return;


    if (callbacks.contains("onTurn")){
        callbacks["onTurn"]();
    }

    if (isWon()) {
        if (callbacks.contains("onWon")) {
            callbacks["onWon"]();
        }
    }
}

bool Board::cubeMove(Cube *cube) {
    SDL_Point new_position{-1,-1};

    SDL_Point currentPosition = cube->getBoardPosition();

    if (currentPosition.x<board_size-1){
        if (!isExistCube(currentPosition.x+1,currentPosition.y)){
            new_position={currentPosition.x+1,currentPosition.y};
        }
    }
    if (currentPosition.x>0){
        if (!isExistCube(currentPosition.x-1,currentPosition.y)){
            new_position={currentPosition.x-1,currentPosition.y};
        }
    }
    if (currentPosition.y<board_size-1){
        if (!isExistCube(currentPosition.x,currentPosition.y+1)){
            new_position={currentPosition.x,currentPosition.y+1};
        }
    }
    if (currentPosition.y>0){
        if (!isExistCube(currentPosition.x,currentPosition.y-1)){
            new_position={currentPosition.x,currentPosition.y-1};
        }
    }

    if (new_position.x==-1) return false;

    float cube_x = 10+this->x+(new_position.x*(board_size_cube+board_spacing));
    float cube_y = 10+this->y+(new_position.y*(board_size_cube+board_spacing));

    cube->move(cube_x,cube_y);
    cube->setBoardPosition(new_position.x,new_position.y);

    return true;
}


bool Board::isExistCube(int pos_x, int pos_y){
    for (auto cube : listCubes){
        if (cube->getBoardPosition().x==pos_x and cube->getBoardPosition().y==pos_y) return true;
    }
    return false;
}

Cube* Board::searchCube(int pos_x, int pos_y){
    for (auto cube : listCubes){
        if (cube->getBoardPosition().x==pos_x and cube->getBoardPosition().y==pos_y) return cube;
    }
    return nullptr;
}

void Board::event(const SDL_Event &event) {
    IWidget::event(event);

    for (auto cube : listCubes){
        cube->event(event);
    }
}

// перемешать
bool Board::intermix() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::vector<Cube*> possible;

    Cube* lastCube = nullptr;   // куб который ходил последний раз (чтоб обратно его не ставить)
    SDL_Point nullPosition{board_size-1,board_size-1};     // пустое поле (от него отталкиваться)

    // собрать все кубы правильно
    for (auto cube : listCubes) {
        int pos_y = int((cube->getNumber()-1) / board_size);
        int pos_x = (cube->getNumber()-1) - pos_y * board_size;

        float cube_x = 10 + this->x + (pos_x * (board_size_cube + board_spacing));
        float cube_y = 10 + this->y + (pos_y * (board_size_cube + board_spacing));

        cube->setBoardPosition(pos_x,pos_y);
        cube->move(cube_x,cube_y);
    }

    // перемешать
    while(1) {
        for (int i = 0; i < 100; i++) {
            possible.clear();
            Cube *cube1 = searchCube(nullPosition.x + 1, nullPosition.y);
            Cube *cube2 = searchCube(nullPosition.x - 1, nullPosition.y);
            Cube *cube3 = searchCube(nullPosition.x, nullPosition.y + 1);
            Cube *cube4 = searchCube(nullPosition.x, nullPosition.y - 1);
            if (cube1 != nullptr and cube1 != lastCube) possible.push_back(cube1);
            if (cube2 != nullptr and cube2 != lastCube) possible.push_back(cube2);
            if (cube3 != nullptr and cube3 != lastCube) possible.push_back(cube3);
            if (cube4 != nullptr and cube4 != lastCube) possible.push_back(cube4);

            std::uniform_int_distribution<> distrib(0, possible.size() - 1);
            lastCube = possible.at(distrib(gen));

            nullPosition = lastCube->getBoardPosition();
            cubeMove(lastCube);

        }
        if (!isWon()) break;
    }

    return true;
}

void Board::onTurn(std::function<void()> func) {
    callbacks["onTurn"] = func;
}

void Board::onWon(std::function<void()> func) {
    callbacks["onWon"] = func;
}

// Проверка на выигрышь
bool Board::isWon() {
    bool result = true;

    for (auto cube : listCubes) {
        int pos_y = int((cube->getNumber()-1) / board_size);
        int pos_x = (cube->getNumber()-1) - pos_y * board_size;

        SDL_Point curPos = cube->getBoardPosition();

        if (curPos.x!=pos_x or curPos.y!=pos_y){
            result=false;
            break;
        }

    }

    return result;
}


