#include "SH_servers_tree_widget.h"
#include "ui_SH_servers_tree_widget.h"
#include "SH_client_channel.h"
#include "SH_server_base.h"
#include "SH_base.h"
#include <QMetaEnum>
#include <QInputDialog>
#include <QSettings>
#include <QScrollBar>
#include <piqt.h>
#include "ccm_SHS_shared.h"

const char property_alive[] = "SHS_is_alive";
const char property_address[] = "SHS_address";
REGISTER_VARIANT(ServerAddress);


enum Column {
	cServer,
	cHostname,
	cOS,
	cChannel,
	cIP,
	cVersions,
};


SHServersTreeWidget::SHServersTreeWidget(QWidget * parent): QWidget(parent) {
	qRegisterMetaType<PacketServerInfo>();
	ui = new Ui::SHServersTreeWidget();
	ui->setupUi(this);
	ui->tree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
#ifdef ANDROID
	ui->tree->setStyleSheet("font-size: 18pt;");
#endif
#ifdef MOBILE_VIEW
	grabGesture(Qt::TapAndHoldGesture);
	connect(ui->tree, &QTreeWidget::itemClicked, this, [this](){on_tree_doubleClicked(QModelIndex());});
#endif
	popup_menu.addAction(ui->actionRemove);
	icon_state[0] = QIcon(":/icons/status-off.png");
	icon_state[1] = QIcon(":/icons/status-green.png");
	icon_state[2] = QIcon(":/icons/status-on.png");
	bc_channel = new SHClientChannel();
	bc_channel->setID(generateID());
	CONNECTU_QUEUED(bc_channel, broadcastReceiveEvent, this, broadcastReceiveEvent, this);
	timer_pip = startTimer(10);
	loadServers();
	loading = false;
}


SHServersTreeWidget::~SHServersTreeWidget() {
	clear();
	delete bc_channel;
}


void SHServersTreeWidget::setPultView() {
	ui->tree->setColumnHidden(cHostname, true);
	ui->tree->setColumnHidden(cOS      , true);
	ui->tree->setColumnHidden(cChannel , true);
	ui->tree->setColumnHidden(cVersions, true);
}


void SHServersTreeWidget::selectCurrentServer() {
	auto si = currentServer();
	if (!si.isValid()) return;
	emit serverSelected(si);
}


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


QString SHServersTreeWidget::queryAddress() {
	//if (!channel) return QString();
	QSettings settings("SHS", "ServersTreeWidget");
	QString addr = settings.value("last_address").toString();
	bool ok = false;
	addr = QInputDialog::getText(this, tr("Add server"), tr("Enter address:"), QLineEdit::Normal, addr, &ok);
	if (!ok) return QString();
	return addr;
}


void SHServersTreeWidget::loadServers() {
	clear();
	QSettings settings("SHS", "ServersTreeWidget");
	auto sl = piqDeserialize<PIVector<PacketServerInfo>>(settings.value("servers").toByteArray());
	for (const auto & s: sl) addServer(s);
	fill();
}


void SHServersTreeWidget::saveServers(const PacketServerInfo & to_remove) {
	auto tsl = servers_;
	QSettings settings("SHS", "ServersTreeWidget");
	auto ssl = piqDeserialize<PIVector<PacketServerInfo>>(settings.value("servers").toByteArray());
	for (const auto & s: ssl) {
		bool contains = false;
		for (const auto & t: tsl) {
			if (t.address.hash() == s.address.hash()) {
				contains = true;
				break;
			}
		}
		if (!contains)
			tsl << s;
	}
	tsl.removeWhere([to_remove](const PacketServerInfo & i){return to_remove.address.hash() == i.address.hash();});
	settings.setValue("servers", piqSerialize(tsl));
}


void SHServersTreeWidget::procGesture(QGesture * g) {
	if (!g) return;
	switch (g->gestureType()) {
	case Qt::TapAndHoldGesture: {
		QTapAndHoldGesture * pg = (QTapAndHoldGesture*)g;
		if (pg->state() == Qt::GestureStarted)
			qApp->postEvent(ui->tree->viewport(), new QContextMenuEvent(QContextMenuEvent::Mouse, ui->tree->viewport()->mapFromGlobal(QCursor::pos())));
		} break;
	default:
		break;
	}
}


void SHServersTreeWidget::clear() {
	PIMutexLocker _ml(smutex);
	qDeleteAll(channels.values());
	channels.clear();
	servers_.clear();
	ui->tree->clear();
	changed = false;
}


void SHServersTreeWidget::fill() {
	if (!changed) return;
	changed = false;
	QMetaEnum ose = SHPlatform::staticMetaObject.enumerator(SHPlatform::staticMetaObject.indexOfEnumerator("OSType"));
	QMetaEnum cte = SHNetworkTypes::staticMetaObject.enumerator(SHNetworkTypes::staticMetaObject.indexOfEnumerator("ChannelType"));
	int vpos = ui->tree->verticalScrollBar()->value();
	ui->tree->clear();
	PIMutexLocker _ml(smutex);
	for (const PacketServerInfo & i: servers_) {
		QTreeWidgetItem * ti = new QTreeWidgetItem();
		ti->setData(cServer,   Qt::UserRole, QVariant::fromValue(i));
		ti->setIcon(cServer,   icon_state[0]);
		ti->setText(cServer,   PI2QString(i.name));
		ti->setText(cHostname, PI2QString(i.identify.hostname));
		ti->setText(cOS,       QString("%1 (%2)").arg(ose.valueToKey(i.identify.os_type)).arg(PI2QString(i.identify.os_version)));
		ti->setText(cChannel,  cte.valueToKey(i.channel));
		ti->setText(cIP,       PI2QString(i.address.ipAddress()));
		ti->setText(cVersions, PI2QString(i.displayVersion()));
		ti->setDisabled(!i.compatible());
		ui->tree->addTopLevelItem(ti);
	}
	setIcons();
	ui->tree->verticalScrollBar()->setValue(vpos);
	ui->tree->header()->resizeSections(QHeaderView::ResizeToContents);
	if (!loading) saveServers();
}


void SHServersTreeWidget::setIcons() {
	for (int i = 0; i < ui->tree->topLevelItemCount(); ++i) {
		auto si = ui->tree->topLevelItem(i)->data(cServer, Qt::UserRole).value<PacketServerInfo>();
		bool is_alive = false;
		int icon = 0;
		auto * ch = channels.value(si.address.hash(), nullptr);
		if (ch) {
			is_alive = ch->property(property_alive).toBool();
			if (is_alive) {
				if (si.name.isEmpty()) icon = 2;
				else icon = 1;
			}
		}
		ui->tree->topLevelItem(i)->setIcon(cServer, icon_state[icon]);
	}
}


void SHServersTreeWidget::on_tree_customContextMenuRequested(const QPoint & pos) {
	auto * ti = ui->tree->currentItem();
	if (!ti) return;
	popup_menu.popup(ui->tree->mapToGlobal(pos));
}


void SHServersTreeWidget::on_tree_doubleClicked(QModelIndex) {
	PacketServerInfo info = currentServer();
	if (!info.isValid() || info.connect_address.isEmpty()) return;
	emit serverSelected(info);
}


void SHServersTreeWidget::on_buttonAddServer_clicked() {
#ifdef Q_OS_ANDROID
	QSettings settings("SHS", "ServersTreeWidget");
	emit addressRequest(settings.value("last_address").toString());
#else
	QString addr = queryAddress();
	addressEntered(addr);
#endif
}


void SHServersTreeWidget::on_buttonUpdate_clicked() {
	auto it = channels.makeIterator();
	while (it.next()) {
		auto * ch = it.value();
		if (!ch) continue;
		ch->setProperty(property_alive, false);
		ch->dataStop();
		ch->connectToServer(ch->property(property_address).value<ServerAddress>());
	}
	setIcons();
}


void SHServersTreeWidget::on_actionRemove_triggered() {
	auto * ti = ui->tree->currentItem();
	if (!ti) return;
	auto si = ti->data(cServer, Qt::UserRole).value<PacketServerInfo>();
	uint addr_hash = si.address.hash();
	auto * ch = channels.value(addr_hash, nullptr);
	if (ch) {
		ch->dataStop();
		delete ch;
	}
	channels.remove(addr_hash);
	servers_.removeWhere([addr_hash](const PacketServerInfo & i){return i.address.hash() == addr_hash;});
	delete ti;
	saveServers(si);
}


void SHServersTreeWidget::addressEntered(QString addr) {
	if (addr.isEmpty()) return;
	QSettings settings("SHS", "ServersTreeWidget");
	settings.setValue("last_address", addr);
	requesting = true;
	PacketServerInfo info;
	info.connect_address = Q2PIString(addr);
	info.address = parseServerAddress(addr);
	addServer(info);
	/*channel->dataStop();
	channel->connectToServer(addr);*/
}


void SHServersTreeWidget::connected() {
	if (!requesting) return;
	channel->dataSend(SHNetworkTypes::makeHeader(SHNetworkTypes::ServerInfoRequest));
}


void SHServersTreeWidget::receiveEvent(PIByteArray data) {
	if (!requesting) return;
	piCout << "SHServersTreeWidget::receiveEvent" << data.toHex();
	PacketHeader hdr = SHNetworkTypes::takeHeader(data);
	if (hdr.type < 0) return;
	switch ((SHNetworkTypes::PacketType)hdr.type) {
	case SHNetworkTypes::ServerInfo: {
		PacketServerInfo info;
		data >> info;
		info.address = channel->address();
		info.connect_address = channel->address().fullAddress();
		info.channel = channel->address().channel;
		addServer(info);
		channel->dataStop();
		requesting = false;
		} break;
	default: break;
	}
}


void SHServersTreeWidget::addServer(const PacketServerInfo & info) {
	if (info.connect_address.isEmpty()) return;
	PIMutexLocker _ml(smutex);
	for (const PacketServerInfo & i: servers_) {
		if (i.address.hash() == info.address.hash())
			return;
	}
	if (!loading) {
		QMetaObject::invokeMethod(this, "fill", Qt::QueuedConnection);
	}
	servers_ << info;
	changed = true;
	auto *& channel(channels[info.address.hash()]);
	if (channel) return;
	channel = new SHClientChannel(true);
	channel->setID(generateID());
	channel->setProperty(property_address, PIVariant::fromValue(info.address));
	CONNECTL(channel, connected, ([this,channel](int){
		QMetaObject::invokeMethod(this, [channel](){
			//piCout << "tree" << "connected";
			channel->dataSend(SHNetworkTypes::makeHeader(SHNetworkTypes::ServerInfoRequest));
		}, Qt::QueuedConnection);
	}));
	CONNECTL(channel, dataReceived, ([this,channel,info](PIByteArray data){
		QMetaObject::invokeMethod(this, [this,channel,info,data](){
			PIByteArray rec_data = data;
			PacketHeader hdr = SHNetworkTypes::takeHeader(rec_data);
			//piCout << "tree" << "received" << hdr.type;
			if (hdr.type == SHNetworkTypes::ServerInfo) {
				PacketServerInfo rec_info;
				rec_data >> rec_info;
				uint cur_addr_hash = info.address.hash();
				PIMutexLocker _ml(smutex);
				for (int i = 0; i < servers_.size_s(); ++i) {
					//piCout << "tree" << "check" << servers_[i].address.fullAddress() << info.address.fullAddress();
					if (servers_[i].address.hash() == cur_addr_hash) {
						servers_[i].name = rec_info.name;
						servers_[i].identify = rec_info.identify;
						servers_[i].channel = channel->address().channel;
						channel->setProperty(property_alive, true);
						//piCout << "tree" << "update" << rec_info.name;
						break;
					}
				}
				changed = true;
				QMetaObject::invokeMethod(this, "fill", Qt::QueuedConnection);
				//piCout << "tree" << "fill request";
			}
			channel->stop();
		}, Qt::QueuedConnection);
	}));
	channel->connectToServer(info);
}


void SHServersTreeWidget::startBroadcast() {
	bc_channel->start();
}


void SHServersTreeWidget::stopBroadcast() {
	bc_channel->stop();
}


PIVector<PacketServerInfo> SHServersTreeWidget::servers() const {
	PIMutexLocker _ml(smutex);
	PIVector<PacketServerInfo> ret = servers_;
	return ret;
}


PacketServerInfo SHServersTreeWidget::currentServer() const {
	QTreeWidgetItem * ci = ui->tree->currentItem();
	if (!ci)
		return PacketServerInfo();
	return ci->data(cServer, Qt::UserRole).value<PacketServerInfo>();
}


bool SHServersTreeWidget::event(QEvent * e) {
	if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) {
		double icon_scale = iconSizeMul;
#ifndef MOBILE_VIEW
		icon_scale *= 0.7;
#endif
		ui->tree->setIconSize(preferredIconSize(icon_scale, this));
		ui->buttonAddServer->setIconSize(preferredIconSize(icon_scale, this));
		ui->buttonUpdate->setIconSize(preferredIconSize(icon_scale, this));
	}
	if (e->type() == QEvent::Gesture) {
		for (QGesture * g: ((QGestureEvent*)e)->gestures())
			procGesture(g);
	}
	return QWidget::event(e);
}


void SHServersTreeWidget::broadcastReceiveEvent(PIByteArray data) {
	PacketHeader hdr = SHNetworkTypes::takeHeader(data);
	//piCout << "rec" << hdr.type;
	if (hdr.type != SHNetworkTypes::Reply) return;
	PacketServerInfo info;
	data >> info;
	info.channel = SHNetworkTypes::TCP;
	info.address = parseServerAddress(PI2QString(info.connect_address));
	addServer(info);
}
