#include "pultwindow_base.h"
#include "keyform.h"
#include <QObject>
#include <QDebug>
#include <QBuffer>
#include <QMenu>
#include <QTimer>
#include <QResource>
#include <QDir>
#include <QWidget>
#include <QMessageBox>
#include <QApplication>
#include <QBoxLayout>
#include <QDesktopServices>
#include <QInputDialog>
#include <QClipboard>
#include <piqt.h>
#include <aboutwindow.h>
#include <qad_locations.h>
#include "tabwidget.h"
#include "stackedwidget.h"
#include "SH_ui_loader.h"
#include "SH_ui_sync.h"
#include "ui_pultwindow_base.h"
#include "ui_keyform.h"
#include "ccm_SHS_shared.h"


PultWindowBase::PultWindowBase(QWidget * parent): QWidget(parent) {
	ui = new Ui::PultWindowBase();
	ui->setupUi(this);
	ui->treeServers->setPultView();
	setObjectName("PultWindowBase");
	double new_font_pt, old_font_xh = fontMetrics().xHeight();
	//qDebug() << "old" << font() << old_font_xh;
#ifdef ANDROID
	new_font_pt = 14;
#else
	new_font_pt = 12;
#endif
	QFont new_font("DejaVu Sans", new_font_pt);
	setFont(new_font);
	ui->labelStatus->setFont(new_font);
	//qDebug() << "new" << font() << fontMetrics().xHeight();
	RUNTIME->font_scale = (double)fontMetrics().xHeight() / old_font_xh;
	ui->stackedWidget->setCurrentIndex(0);
	session.setFile(QAD::userPath(QAD::ltConfig, "session"));
	downloading = false;
	loader = new SHSUiLoader();
	/*QFont f = labelStatus->font();
	f.setPointSizeF(f.pointSizeF() * 1.5);
	pageStatus->setFont(f);
	pageDownload->setFont(f);
	pageConnect->setFont(f);
	pageKeyForm->setFont(f);*/
	rcc_file.setFileName(QDir::tempPath() + "/shs_tmp.rcc");
	connect(ui->pageKeyForm, SIGNAL(keyProbe(QString)), this, SIGNAL(keyProbe(QString)));
	timer_save = 0;
	copy_link_action = new QAction(QIcon(":/icons/edit-copy.png"), tr("Copy server link"), this);
	copy_link_action->setVisible(false);
	connect(copy_link_action, SIGNAL(triggered()), this, SLOT(copyLinkTriggered()));
	QAction * sep = new QAction();
	sep->setSeparator(true);
	role_menu = new QMenu(tr("Change role"));
	role_menu->setIcon(QIcon(":/icons/system-users.png"));
	pult_menu = new QMenu();
	pult_menu->addAction(copy_link_action);
	pult_menu->addAction(role_menu->menuAction());
	pult_menu->addAction(QIcon(":/icons/dialog-cancel.png"), tr("Disconnect"), this, SLOT(disconnectTriggered()));
	pult_menu->addAction(sep);
	pult_menu->addAction(QIcon(":/icons/dialog-information.png"), tr("About ..."), this, SLOT(aboutTriggered()));
	sep = new QAction();
	sep->setSeparator(true);
	pult_menu->addAction(sep);
	pult_menu->addAction(QIcon(":/icons/application-exit.png"), tr("Exit"), this, SLOT(exitTriggered()));
#ifndef MOBILE_VIEW
	tray.setContextMenu(pult_menu);
	tray.setIcon(QIcon(":/icons/SH_pult_remote.png"));
	tray.show();
	connect(&tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayClicked(QSystemTrayIcon::ActivationReason)));
#endif
#ifdef Q_OS_ANDROID
	connect(ui->treeServers, SIGNAL(addressRequest(QString)), this, SIGNAL(dialogRequest(QString)));
	connect(this, SIGNAL(addressEntered(QString)), ui->treeServers, SLOT(addressEntered(QString)));
#endif
	setStatusText(tr("Idle"));
	ui->treeServers->startBroadcast();
	//timer_download = startTimer(200);
}


PultWindowBase::~PultWindowBase() {
	if (rcc_success)
		QResource::unregisterResource(rcc_file.fileName());
	session.clear(false);
	delete role_menu;
	delete pult_menu;
	delete loader;
#ifndef MOBILE_VIEW
	tray.hide();
#endif
	delete ui;
}


void PultWindowBase::saveSession() {
	session.save();
}


void PultWindowBase::addToExternalSession(SessionManager & sm) {
	sm.addEntry(ui->checkAutoConnectLast);
}


bool PultWindowBase::askForDownloadNewPackage() {
	return QMessageBox::question(nullptr, tr("SHS Pult"), tr("New package available, download from server?"),
								 QMessageBox::Yes, QMessageBox::Cancel) == QMessageBox::Yes;
}


void PultWindowBase::setChannel(SHClientChannel * ch) {
	channel = ch;
}


bool PultWindowBase::event(QEvent * e) {
	if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) {
		ui->pageKeyForm->ui->buttonCommit->setIconSize(preferredIconSize(iconSizeMul, this));
	}
	if (e->type() == QEvent::ActivationChange) {
		active_time.restart();
	}
	return QWidget::event(e);
}


void PultWindowBase::timerEvent(QTimerEvent * e) {
	if (e->timerId() == timer_save && form) {
		if (form->isVisible())
			saveSession();
	}
	QWidget::timerEvent(e);
}


void PultWindowBase::showEvent(QShowEvent * e) {
	QWidget::showEvent(e);
	emit showed();
}


void PultWindowBase::closeEvent(QCloseEvent * e) {
#ifdef MOBILE_VIEW
	if (backTriggered()) {
		e->setAccepted(false);
		return;
	}
#endif
	QWidget::closeEvent(e);
}


void PultWindowBase::setForm(QWidget * f) {
	ui->layoutForm->addWidget(f);
	main_stacked = nullptr;
	for (auto * o: form_objects) {
		if (!o) continue;
		auto * ms = qobject_cast<SHSStackedWidget*>(o);
		if (ms) {
			main_stacked = ms;
			break;
		}
	}
}


void PultWindowBase::unsetForm() {
	saveSession();
	session.clear(false);
	main_stacked = nullptr;
	delete form;
	delete loader;
	loader = new SHSUiLoader();
}


void PultWindowBase::hideEvent(QHideEvent * e) {
	saveSession();
	QWidget::hideEvent(e);
}


void PultWindowBase::setStatusText(const QString & t) {
	ui->labelStatus_0->setText(t);
	ui->labelStatus->setText(t);
#ifndef MOBILE_VIEW
	tray.setToolTip(tr("SHS Pult: %1").arg(t));
#endif
}


void PultWindowBase::connectedGraphics() {
	//qDebug() << "";
}


void PultWindowBase::changeRoleRequestFailed() {
	QMessageBox::critical(nullptr, tr("SHS Pult"), tr("Invaid password!"));
}


void PultWindowBase::changeRoleRequestDone(int index) {
	user_role = index;
	for (auto * a: role_menu->actions())
		a->setChecked(a->data().toInt() == index);
}


void PultWindowBase::on_checkAutoConnectLast_toggled(bool on) {
	emit saveExternalSession();
	if (!on) return;
	if (!channel->isConnected() && !channel->isConnecting())
		init();
}


void PultWindowBase::on_stackedWidget_currentChanged(int index) {
	if (index == 0) ui->treeServers->startBroadcast();
	else ui->treeServers->stopBroadcast();
	if (index == 1) ui->busyIcon->start();
	else ui->busyIcon->stop();
}


void PultWindowBase::on_treeServers_serverSelected(PacketServerInfo info) {
	if (!info.address.server.isEmpty())
		emit connectRequest(PI2QString(info.address.fullAddress()));
}


void PultWindowBase::trayClicked(QSystemTrayIcon::ActivationReason r) {
	if (r != QSystemTrayIcon::Trigger) return;
	if (isVisible() && active_time.elapsed() < 500) {
		hide();
	} else {
		showNormal();
		activateWindow();
		raise();
		setFocus();
	}
}


void PultWindowBase::exitTriggered() {
	emit disconnectRequest();
	emit hideMenuRequest();
	emit exited();
	QApplication::exit();
}


void PultWindowBase::copyLinkTriggered() {
	if (server_info.address.channel != SHNetworkTypes::Cloud) return;
	QString link = "https://shs.tools/pult/?address=";
	link += QUrl::toPercentEncoding(PI2QString(server_info.connect_address));
	QApplication::clipboard()->setText(link);
}


void PultWindowBase::disconnectTriggered() {
	ui->checkAutoConnectLast->setChecked(false);
	emit disconnectRequest();
	emit hideMenuRequest();
	setStatusText(tr("Idle"));
}


void PultWindowBase::aboutTriggered() {
	emit hideMenuRequest();
	AboutWindow::show();
}


void PultWindowBase::connecting(QString addr) {
	setStatusText(tr("Connecting to %1 ...").arg(PI2QString(Q2PIString(addr).elided(24))));
}


void PultWindowBase::connected(PacketServerInfo server) {
	//piCout << "PultWindowBase::connected";
	ui->stackedWidget->setCurrentIndex(1);
	server_info = server;
	copy_link_action->setVisible(server_info.address.channel == SHNetworkTypes::Cloud);
	qDeleteAll(role_menu->actions());
	std::stable_sort(server.user_roles.begin(), server.user_roles.end(), [](const ServerUserRole & f, const ServerUserRole & s){
		return f.privilege < s.privilege;
	});
	for (const auto & r: server.user_roles) {
		auto * a = new QAction(r.displayName());
		a->setCheckable(true);
		a->setData(r.index);
		if (r.privilege == 0) a->setChecked(true);
		connect(a, &QAction::triggered, this, [this, a, r](){
			emit changeRoleRequest(r.index);
			a->setChecked(user_role == a->data().toInt());
		});
		role_menu->addAction(a);
	}
	setStatusText(tr("Check key ..."));
}


void PultWindowBase::fetching() {
	setStatusText(tr("Fetching interface ..."));
}


void PultWindowBase::disconnected() {
	qDeleteAll(role_menu->actions());
	user_role = 0;
	//piCout << "PultWindowBase::disconnected";
	downloading = false;
	if (timer_save) {
		killTimer(timer_save);
		timer_save = 0;
	}
	server_info = PacketServerInfo();
	if (form) unsetForm();
	form = 0;
	form_objects.clear();
	setStatusText(tr("Idle"));
	ui->stackedWidget->setCurrentIndex(0);
	copy_link_action->setVisible(false);
	if (ui->checkAutoConnectLast->isChecked())
		QTimer::singleShot(2000, this, [this](){
			if (!channel) return;
			if (!channel->isConnected() && !channel->isConnecting())
				init();
		});
}


void PultWindowBase::receivedRCC(QByteArray rcc) {
	if (rcc_success)
		QResource::unregisterResource(rcc_file.fileName());
	//qDebug() << "receivedRCC" << rcc.size();
	rcc_file.open(QIODevice::ReadWrite);
	rcc_file.resize(0);
	rcc_file.write(rcc);
	rcc_file.close();
	rcc_success = QResource::registerResource(rcc_file.fileName());
}


void PultWindowBase::receivedUI(QByteArray ba) {
	textStatus(tr("Create UI ..."));
	QWidget * frm = loader->load(ba, nullptr, true);
	form_objects = loader->loaded_widgets;
	if (frm == 0) {
		serverError(tr("Can`t load UI!"), false);
		disconnectTriggered();
		return;
	}
	if (form) unsetForm();
	form = frm;
	addToSession();
	setForm(form);
	loader->adjust(form);
	emit formLoaded((QObject*)form, loader);
	ui->stackedWidget->setCurrentIndex(4);
	setStatusText(tr("Connected to \"%1\" (%2)").arg(PI2QString(server_info.name), PI2QString(server_info.address.ipAddress())));
	QSettings settings("SHS", "Pult");
	settings.setValue("last_address", PI2QString(server_info.address.fullAddress()));
	ui->treeServers->addServer(server_info);
}


#define ADD(CN) {CN * co = qobject_cast<CN*>(o); if (co) session.addEntry(co);}
void PultWindowBase::addToSession() {
	session.clear(false);
	foreach (QObject * o, form_objects) {
		QString cn = o->metaObject()->className();
		ADD(QTabWidget);
		ADD(SHSTabWidget);
		ADD(QStackedWidget);
		ADD(SHSStackedWidget);
	}
	session.load();
	if (timer_save > 0) {
		killTimer(timer_save);
		timer_save = 0;
	}
	timer_save = startTimer(1000);
}
#undef ADD


void PultWindowBase::keyRequest(PacketServerInfo server) {
	ui->stackedWidget->setCurrentIndex(3);
	ui->pageKeyForm->ui->labelServerName->setText(PI2QString(server.name));
	ui->pageKeyForm->ui->lineKey->setFocus();
}


void PultWindowBase::keyProbed(bool ok) {
	ui->pageKeyForm->ui->lineKey->clear();
	ui->pageKeyForm->ui->buttonCommit->blinkYesOrNo(ok);
	if (ok) {
		ui->stackedWidget->setCurrentIndex(1);
		//piCout << "show status probed";
		QApplication::inputMethod()->hide();
	}
}


void PultWindowBase::syncVersionFailed(int rec_version) {
	serverError(tr("Mismatch version!\nMy %1 != %2 server!").arg(SHS_UISYNC_VERSION).arg(rec_version), false);
	disconnectTriggered();
}


void PultWindowBase::textStatus(QString t) {
	setStatusText(t);
	qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
}


void PultWindowBase::packageUpdating() {
}


void PultWindowBase::packageNoSuchOnServer(bool forced) {
	serverError(tr("No package on server!\nCall to administrator"), !forced);
}


void PultWindowBase::packageExists(QString path) {
	serverError(tr("Please, install new package"));
}


void PultWindowBase::packageDownloadStarted() {
	ui->stackedWidget->setCurrentIndex(2);
	downloading = true;
}


void PultWindowBase::packageDownloadFinished() {
	ui->stackedWidget->setCurrentIndex(1);
	downloading = false;
}


void PultWindowBase::packageReady(QString path, bool forced) {
	QString msg = tr("New package downloaded to\n\"%1\"").arg(path);
	if (!forced)
		msg += "\n" + tr("Please, install new package");
	QMessageBox::information(0, tr("SHS Pult"), msg);
	if (forced) {
		if (form) ui->stackedWidget->setCurrentIndex(4);
		return;
	}
	QDesktopServices::openUrl(QUrl::fromLocalFile(path));
	qApp->exit();
}


void PultWindowBase::serverError(QString msg, bool critical) {
	QMessageBox::critical(0, tr("SHS Pult"), msg);
	if (critical)
		qApp->exit();
}


void PultWindowBase::init() {
	if (!ui->checkAutoConnectLast->isChecked()) return;
	QSettings settings("SHS", "Pult");
	QString addr = settings.value("last_address").toString();
	if (!addr.isEmpty())
		emit connectRequest(addr);
}
