#include <QMainWindow>
#include <QWidget>
#include <QToolButton>
#include <algorithm>
#include <cstdint>
#include <ctime>
#include <cstdlib>
#include <qabstractitemview.h>
#include <qboxlayout.h>
#include <qchar.h>
#include <qcoreevent.h>
#include <qlabel.h>
#include <qlayoutitem.h>
#include <qlistview.h>
#include <qmainwindow.h>
#include <qnamespace.h>
#include <qscrollarea.h>
#include <qstandarditemmodel.h>
#include <qstringalgorithms.h>
#include <qstringlistmodel.h>
#include <qtoolbutton.h>
#include <Qt>
#include <random>
#include <qwidget.h>
#include <QStandardItemModel>
#include <QDropEvent>
#include <QDrag>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QShortcut>

#include "settings.h"
#include "mdemList.h"
#include "trainWindow.h"
#include "parser.h"

enum ElementState {
	NEUTRAL = 0,
	CORRECT,
	INCORRECT
};

const auto listStyle = 
	"QListView {"
	"   background-color: white;"
	"   border: 0px solid black;"
	"}"
	"QListView::item::first {"
	"   padding: 5px 0 0 0;"
	"}"
	"QListView::item {"
	"   color: black;"
	"   padding: 5px;"
	"   background-color: none;"
	"}"
	"QListView::item:selected {"
	"   background-color: transparent;"
	"	padding-left: 0px;"
	"	margin-left: 0px;"
	"}"
	"QListView::item:hover {"
	"   background-color: lightyellow;"
	"	padding-left: 0px;"
	"	margin-left: 0px;"
	"}";

class CustomItemDelegate : public QStyledItemDelegate {
public:
    CustomItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}

    // Override the paint method to apply custom styling
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
	    painter->save(); 
		Qt::GlobalColor color;
		auto colorIdx = index.data(Qt::UserRole + 1).toInt() ;
		switch (colorIdx) {
			case NEUTRAL: {
				color = Qt::lightGray;
			} break;
			case INCORRECT: {
				color = Qt::red;
			} break;
			case CORRECT: {
				color = Qt::green;
			} break;
		}
		painter->setOpacity(0.5);
		if (color) {
			painter->fillRect(option.rect, color);
		}
	    painter->restore();  
		QStyledItemDelegate::paint(painter, option, index);
    }
};

class MultiChoiceListView : public QListView
{
    Q_OBJECT

public:
    explicit MultiChoiceListView(QWidget *parent = nullptr) : QListView(parent) {
		setStyleSheet(listStyle);
		setAcceptDrops(true);
		setEditTriggers(QAbstractItemView::NoEditTriggers);
		setFocusPolicy(Qt::NoFocus);
		setSelectionMode(QAbstractItemView::NoSelection);
	}
};

class OrderListView : public QListView
{
    Q_OBJECT

public:
    explicit OrderListView(QWidget *parent = nullptr) : QListView(parent) {
		setStyleSheet(listStyle);
		setAcceptDrops(true);
		setFocusPolicy(Qt::NoFocus);
		setEditTriggers(QAbstractItemView::NoEditTriggers);
		setDragDropMode(QAbstractItemView::InternalMove);
	}

protected:
    void dropEvent(QDropEvent *event) override
    {
        QModelIndex sourceIndex = currentIndex();
        QModelIndex targetIndex = indexAt(event->pos());

        if (sourceIndex.isValid() && targetIndex.isValid()) {
            QStandardItemModel *model = qobject_cast<QStandardItemModel *>(this->model());
            if (model) {
                QVariant sourceData = model->data(sourceIndex);
                QVariant targetData = model->data(targetIndex);
                model->setData(sourceIndex, targetData);
                model->setData(targetIndex, sourceData);
                event->ignore();
            }
        } else {
			event->ignore();
        }
    }
};

class MoveListView : public QListView
{
    Q_OBJECT

public:
    explicit MoveListView(QWidget *parent = nullptr) : QListView(parent) {
		setStyleSheet(listStyle);
		setAcceptDrops(true);
		setDragDropMode(QAbstractItemView::DragDrop);
		setDefaultDropAction(Qt::MoveAction);
		setEditTriggers(QAbstractItemView::NoEditTriggers);
		setFocusPolicy(Qt::NoFocus);
	}

protected:
    void dragEnterEvent(QDragEnterEvent *event) override {
        event->acceptProposedAction();
    }

    void dragMoveEvent(QDragMoveEvent *event) override {
        event->acceptProposedAction();
    }

    void dropEvent(QDropEvent *event) override {
		QListView *source = qobject_cast<QListView *>(event->source());
        QStandardItemModel *sourceModel = qobject_cast<QStandardItemModel *>(source->model());
        QStandardItemModel *targetModel = qobject_cast<QStandardItemModel *>(this->model());

        if (source != this) {
            QList<QStandardItem *> items = sourceModel->takeRow(source->currentIndex().row());
            targetModel->appendRow(items);
        }

        event->ignore();
    }
};

#include "trainWindow.moc"

struct GroupView {
	QWidget            widget;
	QVBoxLayout        vWidget;
	QLabel             label;
	QScrollArea        itemScroll;
	QStandardItemModel itemModel;
	MoveListView       itemList;
};

#define ITEM_POOL_CHUNK 200

// Main components
QWidget     *trainWidget;
QVBoxLayout *vTrainWidget;
CustomItemDelegate *itemDelegate;

// Question
QWidget     *questionBox;	
QVBoxLayout *vQuestionBox;
QLabel      *lQuestionText;

// Bottom buttons
QVBoxLayout *vButtonBox;
QWidget     *actionButtons;
QHBoxLayout *hButtons;
QSpacerItem *leftSpacer;
QToolButton *btnTriggerAnswer;
QSpacerItem *rightSpacer;
QToolButton *btnNext;

// Answer question
QLabel      *answerText;

// Multi-choice question
QStandardItemModel *multiChoiceModel;
QListView          *multiChoiceList;

// Order question
OrderListView      *orderList;
QStandardItemModel *orderModel;

// Group question
QStandardItemModel               *groupItemModel;
QListView                        *groupItemList;
QWidget                          *wGroupQuestion;
QVBoxLayout                      *vGroups;
std::vector<QStandardItemModel*>  groupModels;

// Questions & State
MdemBuffer *practiceBuffer;

int32_t currentQuestionIndex = -1;
std::vector<GroupView*> groupViews;
PracticeAlgorithm practiceAlgoritm;
time_t lastTrainedAt;

std::default_random_engine  rng;
std::vector<QStandardItem*> itemPool;

QToolButton *btnNotRemembered;
QToolButton *btnHard;
QToolButton *btnMedium;
QToolButton *btnEasy;

QStandardItem* acquireItem() {
	if (itemPool.size() <= 0) {
		for (size_t i = 0; i < ITEM_POOL_CHUNK; ++i) {
			itemPool.push_back(new QStandardItem);
		}
	}
	auto item = itemPool.back();
	itemPool.pop_back();
	return item;
}

void releaseItem(QStandardItem** item) {
	itemPool.push_back(*item);
	(*item) = nullptr;
}

void releaseAllItems() {
	auto releaseAllInModel = [](QStandardItemModel *model) {
		auto itemCount = model->rowCount();
		for (size_t i = 0; i < itemCount; ++i) {
			auto item = model->item(0);
			model->takeRow(item->row());
			releaseItem(&item);
		}
	};
	releaseAllInModel(multiChoiceModel);
	releaseAllInModel(groupItemModel);
	releaseAllInModel(orderModel);
	for (auto groupView: groupViews) {
		releaseAllInModel(&groupView->itemModel);
	}
}

void hideQuestionElements() {
	lQuestionText->hide();
	answerText->hide();
	orderList->hide();
	multiChoiceList->hide();
	wGroupQuestion->hide();

	btnNotRemembered->hide();
	btnHard->hide();
	btnMedium->hide();
	btnEasy->hide();
	btnTriggerAnswer->hide();
}

void showFeedBackButtons() {
	btnNotRemembered->show();
	btnHard->show();
	btnMedium->show();
	btnEasy->show();
}

void setupAnswerQuestion(MultiElementQuestion *question) {
	lQuestionText->setText(
		QString::fromStdString(question->questionText)
	);
	lQuestionText->show();
	auto ss = std::stringstream();
	for (auto answerEl: question->choices) {
		ss << std::format("- {}", answerEl.answer) << std::endl;
	}
	answerText->setText(
		QString::fromStdString(ss.str())
	);
	if (answerText->isVisible()) {
		answerText->hide();
	}	

	auto checkAnswerClicked = []() {
		answerText->show();
		btnTriggerAnswer->hide();
		if (practiceAlgoritm == SPACED) {
			showFeedBackButtons();
		}
	};
	QObject::connect(
		btnTriggerAnswer,
		&QToolButton::clicked,
		[checkAnswerClicked](bool checked) {
			checkAnswerClicked();
		}
	);		
	btnTriggerAnswer->show();
}

void setupOrderQuestion(MultiElementQuestion *question) {
	lQuestionText->setText(
		QString::fromStdString(question->questionText)
	);
	lQuestionText->show();
	orderModel->clear();
	auto shuffledAnswers = question->choices;
	std::shuffle(shuffledAnswers.begin(), shuffledAnswers.end(), rng);
	for (auto answerEl: shuffledAnswers) {
		auto *item = acquireItem();
		item->setData(NEUTRAL, Qt::UserRole + 1);
		item->setData(QVariant(), Qt::CheckStateRole); 
		item->setCheckable(false);
		item->setText(QString::fromStdString(answerEl.answer));
		orderModel->appendRow(item);
	}
	orderList->show();
	QObject::connect(
		btnTriggerAnswer,
		&QToolButton::clicked,
		[question](bool checked) {
			for (size_t i = 0; i < orderModel->rowCount(); ++i) {
				auto item = orderModel->item(i, 0);
				auto text = item->text();
				auto isCorrect = question->choices[i].answer.compare(text.toStdString()) == 0;
				if (isCorrect) {
					item->setData(CORRECT, Qt::UserRole + 1);
				} else {
					item->setData(INCORRECT, Qt::UserRole + 1);
				}
				orderList->update();
			}
			btnTriggerAnswer->hide();

			if (practiceAlgoritm == SPACED) {
				showFeedBackButtons();
			}
		}
	);		
	btnTriggerAnswer->show();
}

void setupMultiChoiceQuestion(MultiElementQuestion *question) {
	lQuestionText->setText(
		QString::fromStdString(question->questionText)
	);
	lQuestionText->show();

	multiChoiceModel->clear();
	for (auto answerEl: question->choices) {
		auto *item = acquireItem();
		item->setText(QString::fromStdString(answerEl.answer));
		item->setData(NEUTRAL, Qt::UserRole + 1);
		item->setCheckable(true);
		item->setCheckState(Qt::CheckState::Unchecked);
		multiChoiceModel->appendRow(item);
	}
	multiChoiceList->show();

	QObject::connect(
		btnTriggerAnswer,
		&QToolButton::clicked,
		[question](bool checked) {
			for (size_t i = 0; i < multiChoiceModel->rowCount(); ++i) {
				auto item = multiChoiceModel->item(i, 0);
				auto isCorrect = question->choices[i].isCorrect == (item->checkState() == Qt::Checked);
				if (isCorrect) {
					item->setData(CORRECT, Qt::UserRole + 1);
				} else {
					item->setData(INCORRECT, Qt::UserRole + 1);
				}
				multiChoiceList->update();
			}
			btnTriggerAnswer->hide();
			if (practiceAlgoritm == SPACED) {
				showFeedBackButtons();
			}
		}
	);		
	btnTriggerAnswer->show();
}

void setupGroupQuestion(GroupQuestion *question) {
	auto groupSpacer = new QSpacerItem(
		50, 50,
		QSizePolicy::Minimum,
		QSizePolicy::Expanding
	);

	lQuestionText->setText(
		QString::fromStdString(question->questionText)
	);
	lQuestionText->show();
	wGroupQuestion->show();

	for (auto group: question->groups) {
		for (auto itemText: group.elements) {
			auto *qItem = acquireItem();
			qItem->setData(NEUTRAL, Qt::UserRole + 1);
			qItem->setCheckable(false);
			qItem->setData(QVariant(), Qt::CheckStateRole); 
			qItem->setText(QString::fromStdString(itemText));
			groupItemModel->appendRow(qItem);
		}
	}
	groupItemList->update();

	auto makeGroup = []() {
		auto groupView = new GroupView;
		vGroups->addWidget(&groupView->label);
		vGroups->addWidget(&groupView->itemList);
		groupView->itemList.setModel(&groupView->itemModel);
		groupView->itemList.setMaximumHeight(100);
		groupView->itemList.setItemDelegate(itemDelegate);
		groupView->widget.setLayout(&groupView->vWidget);
		groupView->widget.layout()->addWidget(&groupView->label);
		groupView->widget.layout()->addWidget(&groupView->itemList);
		return groupView;
	};

	for (int k = 0; k < groupViews.size(); k++) {
		groupViews[k]->widget.hide();	
	}
	for (size_t i = 0; i < question->groups.size(); i++) {
		GroupView *groupView;
		if (i < groupViews.size()) {
			groupView = groupViews[i];
		} else {
			groupView = makeGroup();
			groupViews.push_back(groupView);
		}
		groupView->label.setText(
			QString::fromStdString(question->groups[i].name)
		);
		vGroups->addWidget(&groupView->widget);
		groupView->widget.show();
	}
	vGroups->addItem(groupSpacer);

	QObject::connect(
		btnTriggerAnswer,
		&QToolButton::clicked,
		[question](bool checked) {
			for (size_t i = 0; i < groupItemModel->rowCount(); ++i) {
				auto item = groupItemModel->item(i, 0);
				item->setData(INCORRECT, Qt::UserRole + 1);
			}
			for (size_t i = 0; i < groupViews.size(); i++) {
				auto groupView = groupViews[i];
				auto group = question->groups[i];
				for (int j = 0; j < groupView->itemModel.rowCount(); ++j) {
					auto item = groupView->itemModel.item(j, 0);
					auto itemText = item->text().toStdString();
					bool found = false;
					for (int k = 0; k < group.elements.size(); ++k) {
						if (group.elements[k].compare(itemText) == 0) {
							found = true;
							break;
						}
					}
					if (found) {
						item->setData(CORRECT, Qt::UserRole + 1);
					} else {
						item->setData(INCORRECT, Qt::UserRole + 1);
					}
				} 
				groupView->itemList.update();
			}
			btnTriggerAnswer->hide();
			if (practiceAlgoritm == SPACED) {
				showFeedBackButtons();
			}
		}
	);		
	btnTriggerAnswer->show();
}

void setupQuestion(Question *question) {
	hideQuestionElements();
	releaseAllItems();
	QObject::disconnect(btnTriggerAnswer, 0, 0, 0);		
	if (auto *q = dynamic_cast<MultiElementQuestion*>(question)) {
		switch (q->type) {
			case MultiElementType::Order:
				setupOrderQuestion(q);
				break;
			case MultiElementType::MultiChoice:
				setupMultiChoiceQuestion(q);
				break;
			case MultiElementType::Regular:
				setupAnswerQuestion(q);
				break;
		} 
	} else if (
	   auto *q = dynamic_cast<GroupQuestion*>(question)
	) {
		setupGroupQuestion(q);
	}
}

void updatePaginationVisibility() {
	if (currentQuestionIndex == practiceBuffer->questions.size() - 1) {
		btnNext->hide();
	} else {
		btnNext->show();
	}
}

template <typename T>
int randomIndex(std::vector<T> *vec) {
	srand(time(NULL));
	return rand() % vec->size();
}

time_t getTime() {
	auto now = std::chrono::system_clock::now();
	return std::chrono::duration_cast<std::chrono::seconds>(
		now.time_since_epoch()
	).count();
}

void setupNextQuestion() {
	if (practiceBuffer->questions.size() <= 0) {
		return;
	}
	switch (practiceAlgoritm) {
		case PRIMARY: {
				currentQuestionIndex++;
				if (currentQuestionIndex < practiceBuffer->questions.size()) {
					setupQuestion(practiceBuffer->questions[currentQuestionIndex]);
				}
				updatePaginationVisibility();
		} break;
		case RANDOM: {
				auto questionCandidates = practiceBuffer->questions;
				if (currentQuestionIndex > -1) {
					questionCandidates.erase(questionCandidates.begin() + currentQuestionIndex);
				}
				if (questionCandidates.size() > 0) {
					auto i = randomIndex(&questionCandidates);
					setupQuestion(questionCandidates[i]);
					for (int k = 0; k < practiceBuffer->questions.size(); ++k) {
						if (practiceBuffer->questions[k] == questionCandidates[i]) {
							currentQuestionIndex = k;
							break;
						}
					}
				}
		} break;
		case SPACED: {
				auto questionCandidates = std::vector<Question*>();
				time_t time = getTime();
				auto lastTrainedAt = practiceBuffer->trainedAt;
				practiceBuffer->trainedAt = time;
				for (size_t i = 0; i < practiceBuffer->questions.size(); ++i) {
					auto cooldownSeconds = practiceBuffer->questions[i]->cooldown * 3600;
					auto cooldownEndsAt = lastTrainedAt + cooldownSeconds;
					if (i != currentQuestionIndex && cooldownEndsAt <= time) {
						questionCandidates.push_back(practiceBuffer->questions[i]);
					}
					auto newCooldown = cooldownEndsAt - time;
					if (newCooldown < 0) {
						newCooldown = 0;
					} 
					practiceBuffer->questions[i]->cooldown = (double)newCooldown / 3600;
				}
				if (questionCandidates.size() > 0) {
					auto i = randomIndex(&questionCandidates);
					setupQuestion(
						questionCandidates[randomIndex(&questionCandidates)]
					);
					for (int k = 0; k < practiceBuffer->questions.size(); ++k) {
						if (practiceBuffer->questions[k] == questionCandidates[i]) {
							currentQuestionIndex = k;
							break;
						}
					}
				}
				update(true);
		} break;
	}
}

void initiatePractice(
	MdemBuffer        *mdemBuffer,
	PracticeAlgorithm algorithm
) {
	practiceAlgoritm = algorithm;
	practiceBuffer = mdemBuffer;

	if (practiceBuffer->questions.size() <= 0) {
		return;
	}

	currentQuestionIndex = -1;
	hideQuestionElements();
	updatePaginationVisibility();
	setupNextQuestion();
}

void setCooldownHours(double cooldown) {
	time_t time = getTime();
	practiceBuffer->trainedAt = time;
	auto question = practiceBuffer->questions[currentQuestionIndex];
	question->cooldown = cooldown;
	update(true);
}

QMainWindow *initTrainWindow() {
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    rng = std::default_random_engine(seed);

    auto trainWindow  = new QMainWindow();
	trainWidget  = new QWidget();
	vTrainWidget = new QVBoxLayout();

	trainWidget->setLayout(vTrainWidget);
    trainWindow->setCentralWidget(trainWidget);
	trainWidget->setLayout(vTrainWidget);
	vTrainWidget->setAlignment(Qt::AlignCenter);
	trainWidget->setObjectName("answer-question-widget");

	itemDelegate = new CustomItemDelegate(multiChoiceList);

	auto addShortcut = [trainWindow](QString key, std::function<void()> func) {
		QShortcut* shortcut = new QShortcut(QKeySequence(key), trainWindow);
		QObject::connect(shortcut, &QShortcut::activated, [func]() {
			func();
		});
	};

	{ // Top button menu. 
		auto hTopButtons         = new QHBoxLayout();
		auto topButtons      = new QWidget();
		topButtons->setLayout(hTopButtons);

		auto topLeftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
		auto btnSaveProgress = new QToolButton();
		btnSaveProgress->setText("Save progress");

		hTopButtons->addItem(topLeftSpacer);
		hTopButtons->addWidget(btnSaveProgress);
		vTrainWidget->addWidget(topButtons);

		QObject::connect(btnSaveProgress, &QToolButton::clicked, []() {
			saveMdem();
		});

		QShortcut* shortcutSaveProgress = new QShortcut(QKeySequence("Ctrl+S"), trainWindow);
		QObject::connect(shortcutSaveProgress, &QShortcut::activated, []() {
			saveMdem();
		});
	}


	{ // Make the question box.
		questionBox   = new QWidget();
		vQuestionBox  = new QVBoxLayout();
		questionBox->setLayout(vQuestionBox);
		questionBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
		vQuestionBox->setAlignment(Qt::AlignCenter);
	}

	{ // Make question text. 
		lQuestionText = new QLabel();
		lQuestionText->setText("What is the capital of Latvia?");
		lQuestionText->setWordWrap(true);
		lQuestionText->setStyleSheet(QString(
			"QLabel {"
				"font-size: 20px;"
			"}"
		));
		lQuestionText->setTextInteractionFlags(Qt::TextSelectableByMouse);
		vQuestionBox->addWidget(lQuestionText);
		lQuestionText->hide();
	}

	{ // Make multi-choice list.
		multiChoiceModel = new QStandardItemModel();
		multiChoiceList = new MultiChoiceListView();
		multiChoiceList->setModel(multiChoiceModel);
		multiChoiceList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
		questionBox->layout()->addWidget(multiChoiceList);
		multiChoiceList->hide();
		multiChoiceList->setItemDelegate(itemDelegate);
	}

	{ // Make answer text.
		answerText = new QLabel();
		answerText->setText("- Riga\n- Second line");
		answerText->setWordWrap(true);
		answerText->setStyleSheet(QString(
			"QLabel {"
				"font-size: 15px;"
			"}"
		));
		answerText->setTextInteractionFlags(Qt::TextSelectableByMouse);
		vQuestionBox->addWidget(answerText);
		answerText->hide();
	}

	{ // Make order list.
		orderModel = new QStandardItemModel();
		orderList  = new OrderListView();
		orderList->setModel(orderModel);
		orderList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
		orderList->setItemDelegate(itemDelegate);

		// Connect to handle the drop event properly
		QObject::connect(
			 orderModel,
			 &QStandardItemModel::rowsMoved,
			 [](const QModelIndex &, int sourceRow, int, const QModelIndex &, int destinationRow) {
			     if (sourceRow != destinationRow - 1) {
					 auto *movedItem = orderModel->takeItem(sourceRow);
					 orderModel->insertRow(destinationRow > sourceRow ? destinationRow - 1 : destinationRow, movedItem);
				 }
			}
		);

		questionBox->layout()->addWidget(orderList);
		orderList->hide();
	}

	{ // Make buttons menu. There are left middle and right buttons.
		vButtonBox       = new QVBoxLayout();
		actionButtons    = new QWidget();
		hButtons         = new QHBoxLayout();

		leftSpacer       = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);

		btnTriggerAnswer = new QToolButton();
		btnNotRemembered = new QToolButton();
		QObject::connect(btnNotRemembered, &QToolButton::clicked, []() {
			QString key = SETTING_NOT_REMEMBERED;
			if (settings->contains(key)) {
				setCooldownHours(settings->value(key).toDouble());
			} else {
				setCooldownHours(0);
			}
		});
		btnHard          = new QToolButton();
		QObject::connect(btnHard, &QToolButton::clicked, []() {
			QString key = SETTING_HARD;
			if (settings->contains(key)) {
				setCooldownHours(settings->value(key).toDouble());
			} else {
				setCooldownHours(12);
			}
		});
		btnMedium        = new QToolButton();
		QObject::connect(btnMedium, &QToolButton::clicked, []() {
			QString key = SETTING_MEDIUM;
			if (settings->contains(key)) {
				setCooldownHours(settings->value(key).toDouble());
			} else {
				setCooldownHours(24);
			}
		});
		btnEasy          = new QToolButton();
		QObject::connect(btnEasy, &QToolButton::clicked, []() {
			QString key = SETTING_EASY;
			if (settings->contains(key)) {
				setCooldownHours(settings->value(key).toDouble());
			} else {
				setCooldownHours(48);
			}
		});
		btnNotRemembered->setText("Not remembered (Z)");
		addShortcut("Z", []() {
			if (btnNotRemembered->isVisible()) {
				btnNotRemembered->click();
			}
		});
		btnHard->setText("Hard (X)");
		addShortcut("X", []() {
			if (btnHard->isVisible()) {
				btnHard->click();
			}
		});
		btnMedium->setText("Medium (C)");
		addShortcut("C", []() {
			if (btnMedium->isVisible()) {
				btnMedium->click();
			}
		});
		btnEasy->setText("Easy (V)");
		addShortcut("V", []() {
			if (btnEasy->isVisible()) {
				btnEasy->click();
			}
		});

		btnTriggerAnswer = new QToolButton();

		QShortcut* shortcutShowAnswer = new QShortcut(QKeySequence("Return"), trainWindow);
		QObject::connect(shortcutShowAnswer, &QShortcut::activated, []() {
			btnTriggerAnswer->click();
		});

		rightSpacer      = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
		btnNext          = new QToolButton();

		hButtons->addItem(leftSpacer);
		hButtons->addWidget(btnTriggerAnswer);
		hButtons->addWidget(btnNotRemembered);
		hButtons->addWidget(btnHard);
		hButtons->addWidget(btnMedium);
		hButtons->addWidget(btnEasy);

		hButtons->addItem(rightSpacer);
		hButtons->addWidget(btnNext);
		vButtonBox->addWidget(actionButtons);
		actionButtons->setLayout(hButtons);

		btnTriggerAnswer->setText("Show answer");
		btnTriggerAnswer->hide();

		btnNext->setText("Next");
		QObject::connect(btnNext, &QToolButton::clicked, []() {
			setupNextQuestion();
		});
		questionBox->setObjectName("question-box");
		actionButtons->setStyleSheet(QString(
			"QToolButton {"
				"padding: 4px 5px;"
				"font-size: 15px;"
			"}"
		));
	}

	{ // Make group question view. 
		wGroupQuestion = new QWidget();
		wGroupQuestion->setStyleSheet(
			"border:none;"
		);
		auto hGroupQuestion = new QHBoxLayout();
		wGroupQuestion->setLayout(hGroupQuestion);
		

		// Items on the left.
		auto itemScroll = new QScrollArea();
		auto vItems     = new QVBoxLayout();
		itemScroll->setLayout(vItems);
		groupItemModel    = new QStandardItemModel();
		groupItemList = new MoveListView();
		groupItemList->setItemDelegate(itemDelegate);
		groupItemList->setModel(groupItemModel);
		vItems->addWidget(groupItemList);
		hGroupQuestion->addWidget(itemScroll);

		// Items on the right.
		auto groupScroll = new QScrollArea();
		auto wGroupContainer = new QWidget();
		vGroups = new QVBoxLayout();
		groupScroll->setWidget(wGroupContainer);
		groupScroll->setWidgetResizable(true);
		wGroupContainer->setLayout(vGroups);
		hGroupQuestion->addWidget(groupScroll);


		wGroupQuestion->setStyleSheet("padding: 0; margin: 0;");
		wGroupQuestion->hide();
		questionBox->layout()->addWidget(wGroupQuestion);
	}

	addShortcut("l", []() {
		if (btnNext->isVisible()) {
			btnNext->click();
		}
	});

	vTrainWidget->addWidget(questionBox);
	vTrainWidget->addWidget(actionButtons);
	return trainWindow;
}

