#include "window.h"
#include <QApplication>
#include <QDebug>
#include <QPushButton>
#include <QSlider>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSpacerItem>
#include <QTimer>

Window::Window(QWidget *parent)
    : QWidget{parent}
{
    solveButton = new QPushButton("solve", this);
    connect(solveButton, SIGNAL(clicked()), this, SLOT(solveButtonClicked()));
    clearButton = new QPushButton("Clear", this);
    connect(clearButton, SIGNAL(clicked()), this, SLOT(clearButtonClicked()));

    slider = new QSlider(this);
    slider->setRange(0, 1000);
    slider->setOrientation(Qt::Horizontal);
    slider->setValue(delayTime);
    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(setValue(int)));

    horizontalLayout = new QHBoxLayout(this);
    verticalLayout = new QVBoxLayout();
    layout = new QGridLayout();

    spacer[0] = new QSpacerItem(4, 4, QSizePolicy::Minimum, QSizePolicy::Fixed);
    spacer[1] = new QSpacerItem(4, 4, QSizePolicy::Minimum, QSizePolicy::Fixed);
    spacer[2] = new QSpacerItem(8, 8, QSizePolicy::Minimum, QSizePolicy::Expanding);
    verticalLayout->addItem(spacer[2]);

    verticalLayout->addWidget(solveButton);
    verticalLayout->addWidget(slider);
    verticalLayout->addWidget(clearButton);
    horizontalLayout->addLayout(layout, 5);
    horizontalLayout->addLayout(verticalLayout, 1);

    layout->addItem(spacer[0], 3, 3);
    layout->addItem(spacer[1], 7, 7);
    layout->setSpacing(0);

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            grid[x + y * 9].setParent(this);
            layout->addWidget(&grid[x + y * 9], y + y / 3, x + x / 3);
            // connecting signals & slots
            for (int d = 0; d < 9; d++) // rows and columns
            {
                connect(&grid[x + y * 9], SIGNAL(update(int)), &grid[x + d * 9], SLOT(removeOption(int)));
                connect(&grid[x + y * 9], SIGNAL(undo(int)), &grid[x + d * 9], SLOT(addOption(int)));
                if (&grid[x + y * 9] != &grid[d + y * 9])
                {
                    connect(&grid[x + y * 9], SIGNAL(update(int)), &grid[d + y * 9], SLOT(removeOption(int)));
                    connect(&grid[x + y * 9], SIGNAL(undo(int)), &grid[d + y * 9], SLOT(addOption(int)));
                }
            }
            int a = x / 3 * 3;
            int b = y / 3 * 3;
            for (int x1 = a; x1 < a + 3; x1++) // square
            {
                for (int y1 = b; y1 < b + 3; y1++)
                {
                    if (x1 == x || y1 == y)
                        continue;
                    connect(&grid[x + y * 9], SIGNAL(update(int)), &grid[x1 + y1 * 9], SLOT(removeOption(int)));

                    connect(&grid[x + y * 9], SIGNAL(undo(int)), &grid[x1 + y1 * 9], SLOT(addOption(int)));
                }
            }
        }
    }
}

void Window::solveButtonClicked()
{
    std::list<History> hist;
    bool backtracking = false;
    srand(time(NULL)); // random seed
    while (1)
    {
        delay();
        if (backtracking)
        {
            hist.back().cell->collapsedCellClicked();
            std::vector<int> validNrs;
            for (int i = 0; i < 9; i++)
                if (!hist.back().blocked[i])
                    validNrs.push_back(i);
            if (validNrs.size() == 0)
            {
                if (hist.size() <= 0)
                    return;
                hist.pop_back();
                continue;
            }
            backtracking = false;
            int rndNr = rand() % validNrs.size();        // choose a random number;
            delay();
            hist.back().cell->collapse(validNrs[rndNr]); // collapse cell with chosen number;
            hist.back().blocked[validNrs[rndNr]]++;
            delay();
        }
        std::vector<Cell *> b; // vector of cells with least entropy
        int minEtropy = 10;
        for (auto &cell : grid)
        {
            if (cell.collapsed)
                continue;
            else if (cell.possibleStates < minEtropy)
            {
                minEtropy = cell.possibleStates;
                b.clear();
                b.push_back(&cell);
            }
            else if (cell.possibleStates == minEtropy)
            {
                b.push_back(&cell);
            }
        }
        if (b.size() == 0) // if b is empty -> solved
            return;

        else if (minEtropy == 0)
        {
            backtracking = true;
            continue;
        }

        int rndCell = rand() % b.size(); // choose a random cell
        std::vector<int> validNrs;
        for (int i = 0; i < 9; i++)
        {
            if (!b[rndCell]->blocked[i])
                validNrs.push_back(i);
        }
        int rndNr = rand() % validNrs.size(); // choose a random number;
        hist.push_back(History(b[rndCell], validNrs[rndNr]));
        b[rndCell]->collapse(validNrs[rndNr]); // collapse that random cell with chosen number;
    }
}
inline void Window::delay()
{
    if(!delayTime)
        return;
    repaint();
    QEventLoop loop;
    QTimer t;
    t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
    t.start(delayTime);
    loop.exec();
}
void Window::clearButtonClicked()
{
    for (auto &x : grid)
    {
        if (x.collapsed)
            x.collapsedCellClicked();
    }
    return;
}
void Window::setValue(int s)
{
    delayTime = s;
    return;
}