#include <cstdio>
#include <format>
#include <fstream>
#include <iostream>
#include <qabstractbutton.h>
#include <qlabel.h>
#include <qlayoutitem.h>
#include <qmainwindow.h>
#include <qobjectdefs.h>
#include <qtoolbutton.h>
#include <qwindow.h>
#include <qwindowdefs.h>
#include <regex>
#include <string>

#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QFileSystemModel>
#include <QTreeView>
#include <QSplitter>
#include <QVariant>
#include <QMessageBox>
#include <QScrollArea>
#include <QSpacerItem>
#include <QFile>
#include <QTime>
#include <QRegularExpression>
#include <QStringList>
#include <QListView>
#include <QWindow>
#include <qabstractitemmodel.h>

#include "api.h"
#include "parser.h"
#include "trainWindow.h"

struct Page {
    int start;
    int end;
};

class Mdem {
public:
    QWidget          *wMdem;
    QLabel           *wFrontText;
    QWidget          *wBack;
    QVector<QLabel*>  backLabels;
    QToolButton      *showButton;
    int               labelCount;

    void showBacklabels() {
        for (int i = 0; i < backLabels.size(); ++i) {
            if (i < labelCount) {
                if (!backLabels[i]->isVisible()) {
                    backLabels[i]->show();
                }
            } else {
                if (backLabels[i]->isVisible()) {
                    backLabels[i]->hide();
                }
            }
        }
    }

    Mdem() {
        wMdem = new QWidget();
        QVBoxLayout *vMdem = new QVBoxLayout();
        wMdem->setLayout(vMdem);
        
        QString id = QString("mdem_%1").arg(1);
        wMdem->setObjectName(id);

        // Front
        QWidget *wFront = new QWidget();
        QHBoxLayout *hFront = new QHBoxLayout();
        wFront->setMinimumHeight(60);
        wFront->setLayout(hFront);
		wFront->setProperty("first", "true");
        wMdem->setStyleSheet(QString(
            "QWidget#%1 > QWidget {"
            "border-right:  1px solid gray;"
            "border-bottom: 1px solid gray;"
            "border-left:   1px solid gray;"
            "} "
            "QWidget#%1 > QWidget[first=\"true\"] {"
            "border-top: 1px solid gray;"
            "}"
        ).arg(id));

        // Add Front Content
        wFrontText = new QLabel();
        hFront->addWidget(wFrontText);
        hFront->addStretch(1);
        
        showButton = new QToolButton();
        showButton->setText("Show");
        hFront->addWidget(showButton);

        // Back
        wBack = new QWidget();
        QVBoxLayout *hBack = new QVBoxLayout();
        wBack->setLayout(hBack);
        vMdem->addWidget(wBack);

        // Add Back Content
        for (int i = 0; i < 20; ++i) {
            QLabel *elBackText = new QLabel();
            hBack->addWidget(elBackText);
            backLabels.append(elBackText);
        }

        vMdem->addWidget(wFront);
        vMdem->addWidget(wBack);
        vMdem->setContentsMargins(0, 0, 0, 0);
        vMdem->setSpacing(0);

        wBack->hide();
        wMdem->hide();

        // Connect button to toggle view
        QObject::connect(showButton, &QToolButton::clicked, [this]() {
            if (wBack->isVisible()) {
                wBack->hide();
                showButton->setText("Show");
            } else {
                wBack->show();
                showButton->setText("Hide");
            }
            showBacklabels();
        });
    }
};


QString                 workingPath   = "/home/jorenchik/Code/mdemory/memorybase";
std::string             currentPath   = "";
QList<Mdem*>            mdems         = QList<Mdem*>();
std::vector<Question*>  questions     = std::vector<Question*>();
QLabel                 *deckListLabel;
QVBoxLayout            *hMdemScroll;
QSpacerItem            *mdemSpacer;

const int              PER_PAGE      = 8;
int                    currentPage   = -1;
std::vector<Page>      pages;
QList<QToolButton*>    paginationButtons;
QToolButton*           prevButton;
QToolButton*           firstButton;
QToolButton*           lastButton;
QToolButton*           nextButton; 
QLabel*                paginationLabel;

QToolButton            *load;
QToolButton            *practice;


const std::regex doubleSpaceExp(
	"  ",
	std::regex_constants::ECMAScript | std::regex_constants::icase
);

const std::regex tabExp(
	"\t",
	std::regex_constants::ECMAScript | std::regex_constants::icase
);

const std::regex newLineExp(
	"\n",
	std::regex_constants::ECMAScript | std::regex_constants::icase
);

void CreateMdems(std::vector<Question*>& questions) {
	hMdemScroll->removeItem(mdemSpacer);

    for (Mdem *mdem : mdems) {
        if (mdem->wMdem->isVisible()) {
            mdem->wMdem->hide();
        }
    }

	if (questions.size() > mdems.size()) {
		auto amount = questions.size() - mdems.size(); 
		for (size_t i = 0; i < amount; ++i) {
			auto mdem = new Mdem();
			mdems.append(mdem);
			hMdemScroll->addWidget(mdem->wMdem);
		}
	}

	auto transformAnswer = [](std::string answer) -> std::string {
		answer = std::regex_replace(answer, doubleSpaceExp, " ");
		answer = std::regex_replace(answer, tabExp, "");
		answer = std::regex_replace(answer, newLineExp, "");
		return answer;
	};

	for (size_t i = 0; i < questions.size(); ++i) {
		if (AnswerQuestion* mw = dynamic_cast<AnswerQuestion*>(questions[i])) {
			mdems[i]->wFrontText->setText(
				QString::fromStdString(mw->QuestionText)	
			);
			auto choices = mw->Choices;
			for (size_t k = 0; k < choices.size(); ++k) {
				auto answer = choices[k].Answer;
				answer = transformAnswer(answer);
				answer = std::format("- {}", answer); 
				if (k < mdems[i]->backLabels.size()) {
					mdems[i]->backLabels[k]->setText(QString::fromStdString(answer));
				} else {
					auto label = new QLabel();
					label->setText(QString::fromStdString(answer));
					mdems[i]->backLabels.push_back(label);
					mdems[i]->wBack->layout()->addWidget(label);
				}
			}
		   mdems[i]->labelCount = choices.size();
		}
		if (!mdems[i]->wMdem->isVisible()) {
		   mdems[i]->wMdem->show();
    	}
	}

	hMdemScroll->addItem(mdemSpacer);
}

const int DISTANCE = 2;


void makePages() {
    pages.clear();
	auto len = questions.size();
	for (int i = 0; i < (len / PER_PAGE) + 1; i++) {
		auto startingIndex = PER_PAGE * i ;
		auto amount = PER_PAGE;
		if (i == questions.size() / PER_PAGE) {
            amount = questions.size() % PER_PAGE;
        }
        pages.push_back(Page{startingIndex, startingIndex + amount});
    }
}


void SwitchPage(int pageIdx) {
    currentPage = pageIdx;

    // Hide all pagination buttons
    for (auto& button : paginationButtons) {
        button->hide();
    }

    int l = 0;
    char buffer[50];
    snprintf(buffer, sizeof(buffer), "Page: %d", pageIdx + 1);
    paginationLabel->setText(buffer);

    // Hide widgets in mdems
    for (auto& mdem : mdems) {
        if (mdem->wBack->isVisible()) {
            mdem->wBack->hide();
            mdem->showButton->setText("Show");
        }
    }

    // Update pagination buttons
    for (int k = -DISTANCE; k <= DISTANCE; ++k) {
        if (pageIdx + k >= 0 && pageIdx + k < pages.size()) {
            auto button = paginationButtons[l];
            snprintf(buffer, sizeof(buffer), "%d", pageIdx + k + 1);
            button->setText(buffer);

            if (pageIdx + k != pageIdx) {
                button->show();
            } else {
                button->hide();
            }
            ++l;
        }
    }

    // Handle first and last buttons
    if (pageIdx > 0 && pages.size() > 1) {
        firstButton->show();
    } else {
        firstButton->hide();
    }

    if (pageIdx < pages.size() - 1 && pages.size() > 1) {
        lastButton->show();
    } else {
        lastButton->hide();
    }

    // Handle next and previous buttons
    if (!pages.empty() && currentPage < pages.size() - 1) {
        nextButton->show();
    } else {
        nextButton->hide();
    }

    if (!pages.empty() && currentPage >= 1) {
        prevButton->show();
    } else {
        prevButton->hide();
    }

    // Handle page slice
    const Page& page = pages[pageIdx];

    std::vector<Question*> pageSlice(questions.begin() + page.start, questions.begin() + page.end);
    CreateMdems(pageSlice);
}

void loadMdem() {
	auto file = std::ifstream(currentPath);
	std::string content;
	if (file) {
		std::stringstream buffer;
		buffer << file.rdbuf();
		content = buffer.str();
		auto parseRes = Transpile(content, true);
		for (auto question: questions) {
			delete question;
		}
		questions.clear();
		if (parseRes.error == "") {
			questions = parseRes.value;
			makePages();
			SwitchPage(0);
		} else {
			std::cout << "Compilation error." << std::endl;
			for (auto mdem: mdems) {
				if (mdem->wMdem->isVisible()) {
					mdem->wMdem->hide();
				}
			}
		}
	} else {
		std::cout << std::format("Could not open the file: {}", currentPath) << std::endl;
	}
}

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QMainWindow window;

    QSplitter *hSplitter = new QSplitter();


    // LeftSide
    QWidget          *leftWidget = new QWidget();
    QVBoxLayout      *leftLayout = new QVBoxLayout();
    QLabel           *mdemLabel  = new QLabel("Mdems");
    QFileSystemModel *model      = new QFileSystemModel();
    QTreeView        *mdemList   = new QTreeView();
	mdemSpacer = new QSpacerItem(50, 50, QSizePolicy::Minimum, QSizePolicy::Expanding);
    
    leftWidget->setLayout(leftLayout);
    leftLayout->addWidget(mdemLabel);
    model->setRootPath(workingPath);
	// Hide all columns except the first one
    mdemList->setModel(model);

	QObject::connect(
			mdemList,
			&QTreeView::doubleClicked,
			[model](const QModelIndex &index) {
				auto fileInfo = model->fileInfo(index);
				currentPath = fileInfo.filePath().toStdString();
				loadMdem();
			}
	);

    QModelIndex rootIndex = model->index(
		"/home/jorenchik/Code/mdemory/memorybase"
	);
    mdemList->setRootIndex(rootIndex);
    for (int col = 1; col < model->columnCount(); ++col) {
        mdemList->hideColumn(col);
    }
    model->setHeaderData(0, Qt::Horizontal, QObject::tr("Custom Name"));
    leftLayout->addWidget(mdemList);


    // DeckList 
    QLabel    *deckLabel = new QLabel("Decks");
    QListView *deckList  = new QListView();
    leftLayout->addWidget(deckLabel);
    leftLayout->addWidget(deckList);

    // RightSide
    QWidget     *rightWidget = new QWidget();
    QVBoxLayout *rightLayout = new QVBoxLayout();
    rightWidget->setLayout(rightLayout);

    QWidget *top      = new QWidget();
    QHBoxLayout *hTop = new QHBoxLayout();
    deckListLabel     = new QLabel("Mdem: todo.mdem");
    top->setLayout(hTop);
    rightLayout->addWidget(top);

    hTop->addWidget(deckListLabel);
    hTop->addStretch(1);

    load  = new QToolButton();
    practice = new QToolButton();

    hTop->addWidget(load);
    hTop->addWidget(practice);

    // Buttons
    load->setText("Load");
    practice->setText("Practice");
	QObject::connect(load, &QToolButton::clicked, &loadMdem);

    // Mdems
    QScrollArea *mdemScroll    = new QScrollArea();
    QWidget     *mdemContainer = new QWidget();
    hMdemScroll = new QVBoxLayout();
    mdemScroll->setWidget(mdemContainer);
    mdemScroll->setWidgetResizable(true);
    mdemContainer->setLayout(hMdemScroll);
    rightLayout->addWidget(mdemScroll);
	hMdemScroll->addItem(mdemSpacer);

    // Pagination 
    hSplitter->addWidget(leftWidget);
    hSplitter->addWidget(rightWidget);
    hSplitter->setStretchFactor(0, 1);
    hSplitter->setStretchFactor(1, 3);


	auto pagination = new QWidget();
	auto hPagination = new QHBoxLayout();
	pagination->setLayout(hPagination);

	firstButton = new QToolButton();
	firstButton->setText(QString::fromStdString("<<"));
	hPagination->addWidget(firstButton);
    firstButton->hide();
	QObject::connect(firstButton, &QToolButton::clicked, [](bool checked) {
        if (pages.size() > 0) {
            SwitchPage(0);
        }
    });

	prevButton = new QToolButton();
	prevButton->setText(QString::fromStdString("<"));
	hPagination->addWidget(prevButton);
    prevButton->hide();
	QObject::connect(prevButton, &QToolButton::clicked, [](bool checked) {
        if (pages.size() > 0) {
            SwitchPage(currentPage - 1);
        }
    });

	for (int i = 0; i < PER_PAGE; i++) {
		auto elButton = new QToolButton();
		elButton->setText(QString("%1").arg(i+1));
		hPagination->addWidget(elButton);
        elButton->hide();
		QObject::connect(elButton, &QToolButton::clicked, [elButton](bool checked) {
            auto pageNum = std::stoi(elButton->text().toStdString().c_str());
            auto pageIdx = pageNum - 1;
            if (pageIdx < pages.size()) {
                SwitchPage(pageIdx);
            }
		});
        paginationButtons.push_back(elButton);
	}
    
	nextButton = new QToolButton();
	nextButton->setText(QString::fromStdString(">"));
	hPagination->addWidget(nextButton);
    nextButton->hide();
	QObject::connect(nextButton, &QToolButton::clicked, [](bool checked) {
        if (pages.size() > 0) {
            SwitchPage(currentPage + 1);
        }
    });

	lastButton = new QToolButton();
	lastButton->setText(QString::fromStdString(">>"));
	hPagination->addWidget(lastButton);
    lastButton->hide();
	QObject::connect(lastButton, &QToolButton::clicked, [](bool checked) {
        if (pages.size() > 0) {
            SwitchPage(pages.size() - 1);
        }
    });
	hPagination->addStretch(1);
    paginationLabel = new QLabel();
    hPagination->addWidget(paginationLabel);
	rightLayout->addWidget(pagination);

	initTrainWindow();
	QObject::connect(
		practice,
		&QToolButton::clicked,
		[](bool checked) {
		trainWindow->show();
		trainWindow->resize(600, 300);
	});

    window.setCentralWidget(hSplitter);
    window.show();

    return app.exec();
}
