#include <QInputDialog>
#include <QMessageBox>
#include "server_manager.h"
#include "SH_network_types.h"
#include "SH_server_base.h"
#include "SH_runtime.h"
#include "ccm_SHS_shared.h"


ServerManager::ServerManager(QWidget * parent): QWidget(parent), Ui::ServerManager(), channel(true) {
	setupUi(this);
	stackedWidget->setCurrentIndex(0);
	tabWidgetServer->setCurrentIndex(0);
	widgetParameters->setResetButtonsVisible(false);
	log_category[SHServer::LogInfo   ] = "info" ;
	log_category[SHServer::LogSuccess] = "ok"   ;
	log_category[SHServer::LogWarning] = "warn" ;
	log_category[SHServer::LogError  ] = "error";
	log_category[SHServer::LogScript ] = "qs"   ;
	log_category[SHServer::LogIO     ] = "io"   ;
	logView->registerCategory(tr("Info"   ), log_category[SHServer::LogInfo   ], QImage(":/icons/dialog-information.png"));
	logView->registerCategory(tr("Success"), log_category[SHServer::LogSuccess], QImage(":/icons/dialog-ok-apply.png"   ), Qt::NoBrush, QColor(0xD0FFD0));
	logView->registerCategory(tr("Warning"), log_category[SHServer::LogWarning], QImage(":/icons/dialog-warning.png"    ), Qt::NoBrush, QColor(0xFFFFA0));
	logView->registerCategory(tr("Error"  ), log_category[SHServer::LogError  ], QImage(":/icons/dialog-error.png"      ), Qt::NoBrush, QColor(0xFFD0D0));
	logView->registerCategory(tr("Script" ), log_category[SHServer::LogScript ], QImage(":/icons/code-union.png"        ));
	logView->registerCategory(tr("IO"     ), log_category[SHServer::LogIO     ], QImage(":/icons/packet-io.png"         ));
	logView->setFilterVisible(true);
	channel.setReopenEnabled(true);
	channel.setID(generateID());
	last_ident = PacketIdentification::makeLocal(SHServer::RoleAdmin, SHS_NETWORK_VERSION, SHS_UISYNC_VERSION);
	timer_pip = startTimer(10);
	CONNECTU_QUEUED(&channel, connected, this, connected, this);
	CONNECTU_QUEUED(&channel, disconnected, this, disconnected, this);
	CONNECTU_QUEUED(&channel, dataReceived, this, receiveEvent, this);
	treeServers->startBroadcast();
}


ServerManager::~ServerManager() {

}


void ServerManager::timerEvent(QTimerEvent * e) {
	if (e) {
		if (e->timerId() == timer_pip) {
			maybeCallQueuedEvents();
		}
	}
}


void ServerManager::sendIdentification() {
	PIByteArray ba;
	ba = SHNetworkTypes::makeHeader(SHNetworkTypes::Identification);
	last_ident.admin_password = passwords.value(last_server.name);
	ba << last_ident;
	channel.dataSend(ba);
}


void ServerManager::setVariables(const PIVector<ServerVariable> & variables) {
	for (int i = treeVariables->topLevelItemCount() - 1; i >= variables.size_s(); --i)
		delete treeVariables->takeTopLevelItem(i);
	for (int i = treeVariables->topLevelItemCount(); i < variables.size_s(); ++i)
		treeVariables->addTopLevelItem(new QTreeWidgetItem());
	for (int i = 0; i < treeVariables->topLevelItemCount(); ++i) {
		QTreeWidgetItem * ti = treeVariables->topLevelItem(i);
		const auto & v(variables[i]);
		ti->setText(0, v.name);
		ti->setText(1, QString::number(v.value));
		ti->setText(2, QDateTime::fromMSecsSinceEpoch(v.changed).toString("dd.MM.yyyy  hh:mm:ss"));
	}
}


void ServerManager::waitForReconnect(QString msg) {
	need_reconnect = true;
	labelWait->setText(msg);
	stackedWidget->setCurrentIndex(2);
}


bool ServerManager::askPassword(PIString server_name) {
	bool ok = false;
	QString pass = QInputDialog::getText(this, tr("Connecting to server"), tr("Input admin password:"), QLineEdit::Password, "", &ok);
	if (!ok)
		return false;
	QAD::CursorOverrider curov;
	passwords[server_name] = passwordHash(Q2PIString(pass));
	return true;
}


void ServerManager::connected() {
	if (treeServers->isRequesting()) return;
	sendIdentification();
	treeServers->stopBroadcast();
}


void ServerManager::disconnected() {
	//piCout << "disconnected" << need_reconnect;
	if (stackedWidget->currentIndex() == 1)
		stackedWidget->setCurrentIndex(0);
	treeServers->startBroadcast();
	if (!need_reconnect) return;
	channel.connectToServer(last_server);
}


void ServerManager::receiveEvent(PIByteArray data) {
	if (treeServers->isRequesting()) return;
	//piCout << "packetReceived" << data.size();
	PacketHeader hdr = SHNetworkTypes::takeHeader(data);
	if (hdr.type < 0) return;
	//qDebug() << "packet type" << type;
	switch ((SHNetworkTypes::PacketType)hdr.type) {
	case SHNetworkTypes::NetworkVersionMismatch:
		QMessageBox::critical(this, "Upload baf", "Invalid network version");
		disconnect();
		break;
	case SHNetworkTypes::AdminBAFOk:
		waitForReconnect(tr("Upload done, wait for server ..."));
		break;
	case SHNetworkTypes::AdminBAFFail:
		QMessageBox::critical(this, "Upload baf", "Something goes wrong :-(");
		break;
	case SHNetworkTypes::AdminPasswordFail:
		channel.dataStop();
		if (askPassword(last_server.name))
			channel.connectToServer(last_server);
		else
			disconnect();
		break;
	case SHNetworkTypes::AdminPasswordAccepted:
		stackedWidget->setCurrentIndex(1);
		break;
	case SHNetworkTypes::AdminLog: {
		PacketServerLog pckt;
		data >> pckt;
		for (const auto & l: pckt.entries) {
			QString msg = QString("%1 - %2").arg(l.time.toString(), l.text);
			logView->addText(msg, log_category[l.type]);
		}
		} break;
	case SHNetworkTypes::AdminState: {
		PacketServerState pckt;
		data >> pckt;
		widgetClients->set(pckt.clients);
		widgetDevices->set(pckt.devices);
		setVariables(pckt.variables);
		} break;
	case SHNetworkTypes::AdminParameterList: {
		PacketParameterList pckt;
		data >> pckt;
		parameters.clear();
		param_id.clear();
		for (const auto & p: pckt.parameters) {
			parameters.addProperty(p.name, p.value);
			param_id[p.name] = p.id;
		}
		widgetParameters->setStorage(&parameters);
		} break;
	case SHNetworkTypes::AdminScriptError: {
		PacketScriptError se;
		data >> se;
		if (RUNTIME->server_data.project.propertyValueByName("name").toString() == se.project)
			emit scriptError(se.line, QFileInfo(se.file).fileName(), se.message, se.stack);
		} break;
	case SHNetworkTypes::AdminRestartQueued:
		waitForReconnect(tr("Restarting, wait for server ..."));
		break;
	default: break;
	}
}


void ServerManager::disconnect() {
	need_reconnect = false;
	channel.dataStop();
	stackedWidget->setCurrentIndex(0);
	logView->clear();
}


void ServerManager::on_buttonUploadBAF_clicked() {
	QByteArray baf;
	emit requestBAF(&baf);
	if (baf.isEmpty())
		return;
	PIByteArray ba = SHNetworkTypes::makeHeader(SHNetworkTypes::AdminBAFUpload);
	ba << Q2PIByteArray(baf);
	channel.dataSend(ba);
}


void ServerManager::on_buttonRestart_clicked() {
	PIByteArray ba = SHNetworkTypes::makeHeader(SHNetworkTypes::AdminRestart);
	channel.dataSend(ba);
}


void ServerManager::on_buttonParametersApply_clicked() {
	widgetParameters->applyProperties();
	for (const auto & p: parameters) {
		PacketParameter pckt;
		pckt.id = param_id.value(p.name, -1);
		pckt.value = p.value;
		PIByteArray ba = SHNetworkTypes::makeHeader(SHNetworkTypes::AdminSetParameter);
		ba << pckt;
		channel.dataSend(ba);
	}
}


void ServerManager::on_buttonWaitCancel_clicked() {
	disconnect();
}


void ServerManager::on_treeServers_serverSelected(PacketServerInfo info) {
	if (!passwords.contains(info.name)) {
		if (!askPassword(info.name))
			return;
	}
	need_reconnect = true;
	last_server = info;
	channel.dataStop();
	channel.connectToServer(last_server);
}
