#include <qabstractbutton.h>
#include <qapplication.h>
#include <qboxlayout.h>
#include <qcheckbox.h>
#include <qcombobox.h>
#include <qcoreevent.h>
#include <qdialog.h>
#include <qlabel.h>
#include <qlayoutitem.h>
#include <qmainwindow.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qsettings.h>
#include <qsizepolicy.h>
#include <qspinbox.h>
#include <qt/QtWidgets/qwidget.h>
#include <qtoolbutton.h>
#include <qvalidator.h>
#include <qwidget.h>
#include <qwindow.h>
#include <qwindowdefs.h>

#include <Qsci/qsciscintilla.h>
#include <Qsci/qscilexercpp.h>
#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 <QMenuBar>
#include <QMenu>
#include <QFileDialog>
#include <qabstractitemmodel.h>
#include <QFormLayout>
#include <QSettings>
#include <QLineEdit>
#include <QComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QPushButton>
#include <QStandardPaths>
#include <QShortcut>
#include <QWidget>

#include "mdemList.h"
#include "api.h"
#include "qscilexer.h"

QSettings *settings;

QWidget *initSettings () { 
	QString configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
	QDir().mkpath(configDir);
	QString settingsFile = configDir + "/mdem.ini";
	settings = new QSettings(settingsFile, QSettings::IniFormat);
	auto* settingsWindow = new QWidget;

	settingsWindow->setWindowTitle("Settings");

	auto top = new QWidget;
	auto hlTop = new QVBoxLayout;
	top->setLayout(hlTop);

	auto settingsLabel = new QLabel;
	settingsLabel->setText("Settings");
	settingsLabel->setStyleSheet(
		"font-size: 20px;"
	);
	hlTop->addWidget(settingsLabel);

	auto formLayout = new QFormLayout;

	auto* mbaseInput = new QLineEdit;
	auto* browseButton = new QPushButton("Browse");
	auto* pathLayout = new QHBoxLayout;
	pathLayout->addWidget(mbaseInput);
	pathLayout->addWidget(browseButton);
	formLayout->addRow("Default memorybase:", pathLayout);

	QObject::connect(browseButton, &QPushButton::clicked, [mbaseInput]() {
		QString dir = QFileDialog::getExistingDirectory(
			nullptr,
			"Select Config Directory",
			mbaseInput->text()
		);
		if (!dir.isEmpty()) {
			mbaseInput->setText(dir);
		}
	});

	auto characterWrap = new QSpinBox;
	characterWrap->setRange(30, 150); 
	formLayout->addRow("Character wrap in code gen [30-150]:", characterWrap);

	auto* timezone = new QSpinBox;
	timezone->setRange(-12, 12); 
	formLayout->addRow("Timezone as number (e.g. +2 as 2):", timezone);

	auto* notRemembered = new QDoubleSpinBox;
	notRemembered->setRange(0, 100); 
	formLayout->addRow("Not remembered:", notRemembered);

	auto* hard = new QDoubleSpinBox;
	hard->setRange(0, 100); 
	formLayout->addRow("Hard:", hard);

	auto* medium = new QDoubleSpinBox;
	medium->setRange(0, 100); 
	formLayout->addRow("Medium:", medium);

	auto* easy = new QDoubleSpinBox;
	easy->setRange(0, 100); 
	formLayout->addRow("Easy:", easy);

	auto* debug = new QCheckBox;
	formLayout->addRow("Debug:", debug);

	auto* showTimes = new QCheckBox;
	formLayout->addRow("Show times in the console:", showTimes);

	auto btnLayout = new QHBoxLayout;
	auto btnSaveSettings = new QPushButton("Save");
	auto btnLoad = new QPushButton("Load");
	auto mainLayout = new QVBoxLayout;
	btnLayout->addWidget(btnSaveSettings);
	btnLayout->addWidget(btnLoad);

	// @Improve: validate setting values
	auto setSettingInputs = [
		mbaseInput,
		characterWrap,
		timezone,
		notRemembered,
		hard,
		medium,
		easy,
		debug,
		showTimes
	]() {
		mbaseInput->setText(settings->value(SETTING_MEMORYBASE).toString());
		characterWrap->setValue(settings->value(SETTING_CHARACTER_WRAP).toInt());
		timezone->setValue(settings->value(SETTING_TIMEZONE).toInt());
		notRemembered->setValue(settings->value(SETTING_NOT_REMEMBERED).toDouble());
		hard->setValue(settings->value(SETTING_HARD).toDouble());
		medium->setValue(settings->value(SETTING_MEDIUM).toDouble());
		easy->setValue(settings->value(SETTING_EASY).toDouble());
		debug->setChecked(settings->value(SETTING_DEBUG).toBool() == true);
		showTimes->setChecked(settings->value(SETTING_SHOW_TIMES).toBool() == true);
	};

	auto updateSettingsLabel = [settingsLabel](bool isChanged) {
		if (isChanged) {
			settingsLabel->setText("Settings *");
		} else {
			settingsLabel->setText("Settings");
		}
	};

	auto attachChangeListener = [settingsLabel, updateSettingsLabel](QObject *input, QString key) {
		if (QLineEdit *lineInput = qobject_cast<QLineEdit*>(input)) {
			QObject::connect(
				lineInput,
				&QLineEdit::textChanged,
				[settingsLabel, key, updateSettingsLabel](QString value) {
				if (settings->value(key).toString() != value) {
					updateSettingsLabel(true);
				}
			});
		} else if (QSpinBox *spinBox = qobject_cast<QSpinBox*>(input)) {
			QObject::connect(
				spinBox,
				QOverload<int>::of(&QSpinBox::valueChanged),
				[settingsLabel, key, updateSettingsLabel](int value) {
				if (settings->value(key).toInt() != value) {
					updateSettingsLabel(true);
				}
			});
		} else if (QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(input)) {
			QObject::connect(
				doubleSpinBox,
				QOverload<double>::of(&QDoubleSpinBox::valueChanged),
				[settingsLabel, key, updateSettingsLabel](double value) {
					if (settings->value(key).toDouble() != value) {
						updateSettingsLabel(true);
					}
			});
		} else if (QCheckBox *checkBox = qobject_cast<QCheckBox*>(input)) {
			QObject::connect(
				checkBox,
				QOverload<int>::of(&QCheckBox::stateChanged),
				[settingsLabel, key, updateSettingsLabel](int state) {
					bool value = (state == Qt::Checked);
					if (settings->value(key).toBool() != value) {
						updateSettingsLabel(true);
					}
				}
			);
		}
	};

	attachChangeListener(mbaseInput, SETTING_MEMORYBASE);
	attachChangeListener(characterWrap, SETTING_CHARACTER_WRAP);
	attachChangeListener(timezone, SETTING_TIMEZONE);
	attachChangeListener(notRemembered, SETTING_NOT_REMEMBERED);
	attachChangeListener(hard, SETTING_HARD);
	attachChangeListener(medium, SETTING_MEDIUM);
	attachChangeListener(easy, SETTING_EASY);
	attachChangeListener(debug, SETTING_DEBUG);
	attachChangeListener(showTimes, SETTING_SHOW_TIMES);

	auto saveSettings = [
		mbaseInput,
		characterWrap,
		timezone,
		notRemembered,
		hard,
		medium,
		easy,
		debug,
		showTimes,
		updateSettingsLabel
	]() {
		settings->setValue(SETTING_MEMORYBASE,     mbaseInput->text());
		settings->setValue(SETTING_CHARACTER_WRAP, characterWrap->value());
		settings->setValue(SETTING_TIMEZONE,       timezone->value());
		settings->setValue(SETTING_NOT_REMEMBERED, notRemembered->value());
		settings->setValue(SETTING_HARD,           hard->value());
		settings->setValue(SETTING_MEDIUM,         medium->value());
		settings->setValue(SETTING_EASY,           easy->value());
		settings->setValue(SETTING_DEBUG,          debug->isChecked());
		settings->setValue(SETTING_SHOW_TIMES,     showTimes->isChecked());
		updateSettingsLabel(false);
	};

	auto loadSettings = [setSettingInputs, updateSettingsLabel]() {
		settings->sync();
		setSettingInputs();
		updateSettingsLabel(false);
	};

	QObject::connect(
		btnSaveSettings,
		&QPushButton::clicked,
		[saveSettings]() {
			saveSettings();
		}
	);
	QObject::connect(
		btnLoad,
		&QPushButton::clicked,
		[loadSettings]() {
			loadSettings();
		}
	);
	loadSettings();

	QShortcut* shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), settingsWindow);
	QObject::connect(shortcutSave, &QShortcut::activated, [saveSettings]() {
		saveSettings();
	});

	auto spacer = new QSpacerItem(
		50,
		50,
		QSizePolicy::Expanding,
		QSizePolicy::Expanding
	);

	mainLayout->addWidget(top);
	mainLayout->addLayout(formLayout);
	mainLayout->addItem(spacer);
	mainLayout->addLayout(btnLayout);

	settingsWindow->setLayout(mainLayout);
	return settingsWindow;
}
