#include "piintrospector.h"

#include "ccm_piintrospector.h"
#include "pichunkstream.h"
#include "pidir.h"
#include "pifile.h"
#include "pitime.h"

#include <QClipboard>
#include <QScrollBar>


QPIIntrospector::QPIIntrospector(QWidget * parent): EMainWindow(parent), peer("__introspection_client__") {
	setupUi(this);
	request_timer = 0;
	session.setFile("qpiintrospector_session.conf");
#if QT_VERSION >= 0x050000
	// treeContainers->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
#else
	// treeContainers->header()->setResizeMode(QHeaderView::ResizeToContents);
#endif
	CONNECTU_QUEUED(&peer, peerConnectedEvent, this, peersChanged, this);
	CONNECTU_QUEUED(&peer, peerDisconnectedEvent, this, peersChanged, this);
	CONNECTU_QUEUED(&peer, dataReceivedEvent, this, peerReceived, this);
	// CONNECTU(&peer, peerConnectedEvent, this, reqProcPIEvents);
	// CONNECTU(&peer, peerDisconnectedEvent, this, reqProcPIEvents);
	// CONNECTU(&peer, dataReceivedEvent, this, reqProcPIEvents);
	session.addEntry(this);
	session.addEntry(tabWidgetMain);
	PICodeInfo::EnumInfo * ei = PICODEINFO::enums().value("PIIntrospection::InfoTypes");
	if (ei) {
		for (const auto & e: ei->members) {
#if PIP_VERSION >= PIP_MAKE_VERSION(2, 39, 0)
			QCheckBox * cb = new QCheckBox(PI2QString(e.name.mid(2).toString()));
#else
			QCheckBox * cb = new QCheckBox(PI2QString(e.name.mid(2)));
#endif
			cb->setObjectName(QString("checkRequest%1").arg(e.value));
			cb->setProperty("__value__", e.value);
			layoutRequestFlags->addWidget(cb);
			session.addEntry(cb);
		}
	}
	pip_timer = startTimer(10);
	session.load();
	peer.start();
}


QPIIntrospector::~QPIIntrospector() {
	session.save();
	peer.stop();
}


void QPIIntrospector::changeEvent(QEvent * e) {
	EMainWindow::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange: retranslateUi(this); break;
	default: break;
	}
}


void QPIIntrospector::timerEvent(QTimerEvent * e) {
	if (e->timerId() == pip_timer) callQueuedEvents();
	if (e->timerId() == request_timer) buttonRequest->click();
}


void QPIIntrospector::savingSession(QPIConfig & conf) {
	conf.setValue("treeStat_header", treeStat->header()->saveState());
}


void QPIIntrospector::loadingSession(QPIConfig & conf) {
	treeStat->header()->restoreState(conf.getValue("treeStat_header").toByteArray());
}


void QPIIntrospector::buildTree(QByteArray d) {
	/*PIVector<PIIntrospectionThreads::ThreadInfo> threads;
	PIByteArray pd = Q2PIByteArray(d);
	pd >> threads;
	treeContainers->clear();
	for (const auto & t: threads) {
	    QTreeWidgetItem * ti = new QTreeWidgetItem();
	    ti->setText(0, QString(PI2QString(t.name) + " (%1)").arg(t.id));
	    treeContainers->addTopLevelItem(ti);
	}*/
}


void QPIIntrospector::procRequestTimer() {
	if (request_timer != 0) killTimer(request_timer);
	request_timer = 0;
	if (!checkRequestTimer->isChecked()) return;
	request_timer = startTimer(1000 / spinRequestTimerHz->value());
}


void QPIIntrospector::buildDumpSection(QTreeWidgetItem * pi, PIString & str) {}


void QPIIntrospector::showInfo() {
	PIString s;
	s += info.execCommand + "\n";
	s += info.execDateTime.toString() + "\n";
	s += info.OS_name + "(" + info.OS_version + ", " + info.architecture + ")\n";
	s += info.user + "\n";
	s += info.build_options.join(", ") + "\n";
	s += "\n";
	s += "load k: " + PIString::fromNumber(stat.proc.cpu_load_system, 'f', 2) + " %\n";
	s += "load u: " + PIString::fromNumber(stat.proc.cpu_load_user, 'f', 2) + " %\n";
	s += "threads: " + PIString::fromNumber(stat.proc.threads) + "\n";
	s += "memory usage: " + stat.proc.physical_memsize_readable;
	labelInfo->setText(PI2QString(s));
	int tlic = treeStat->topLevelItemCount();
	for (int i = tlic; i < stat.threads.size_s(); ++i)
		treeStat->addTopLevelItem(new QTreeWidgetItem());
	for (int i = tlic - 1; i >= stat.threads.size_s(); --i)
		delete treeStat->topLevelItem(i);
	tlic = piMini(treeStat->topLevelItemCount(), stat.threads.size_s());
	for (int i = 0; i < tlic; ++i) {
		QTreeWidgetItem * ti = treeStat->topLevelItem(i);
		PISystemMonitor::ThreadStats & ts(stat.threads[i]);
		ti->setText(0, QString::number(i + 1));
		ti->setText(1, QString::number(ts.id));
		ti->setText(2, PI2QString(ts.name));
		ti->setText(3, durationStr(ts.work_time));
		ti->setText(4, QString::number(ts.cpu_load_kernel, 'f', 2));
		ti->setText(5, QString::number(ts.cpu_load_user, 'f', 2));
	}
}


QString QPIIntrospector::durationStr(PISystemTime t) {
	QString ret;
	double s = t.toSeconds();
	int d, h, m;
	d = piFloor(s / (24 * 60 * 60));
	if (d > 0) {
		ret += QString::number(d) + " d ";
		s -= d * (24 * 60 * 60);
	}
	h = piFloor(s / (60 * 60));
	if (h > 0) {
		ret += QString::number(h) + " h ";
		s -= h * (60 * 60);
	}
	m = piFloor(s / 60);
	if (m > 0) {
		ret += QString::number(m).rightJustified(2, '0') + " m ";
		s -= m * 60;
	}
	ret += QString::number(piFloor(s)).rightJustified(2, '0') + " s";
	return ret;
}

#if PIP_VERSION >= PIP_MAKE_VERSION(2, 35, 0)
void QPIIntrospector::prepareContainers(PIVector<PIIntrospectionContainers::TypeInfo> & data) {
	for (PIIntrospectionContainers::TypeInfo & i: data) {
		i.allocated_bytes = i.allocated * i.item_size;
		i.used_bytes      = i.used * i.item_size;
		i.item_size_str.setReadableSize(i.item_size);
		i.allocated_str.setReadableSize(i.allocated_bytes);
		i.used_str.setReadableSize(i.used_bytes);
	}
}
#endif

void QPIIntrospector::on_listApp_currentRowChanged(int r) {
	PIString cs;
	if (r < 0)
		cs.clear();
	else
		cs = Q2PIString(listApp->item(r)->data(Qt::UserRole).toString());
	if (cur_server != cs) widgetContainers->clear();
	cur_server = cs;
}


void QPIIntrospector::peerReceived(const PIString & from, const PIByteArray & data) {
	if (from != cur_server) return;
	// piCout << "rec" << data.size();
	PIByteArray ba(data);
	if (ba.size_s() < 4) return;
	uint sign(0);
	ba >> sign;
	if (sign != PIIntrospection::sign) return;
	PIChunkStream cs(ba);
	PIByteArray pba;
	while (!cs.atEnd()) {
		switch (cs.read()) {
		case PIIntrospection::itInfo: {
			pba.clear();
			cs.get(pba);
			PIIntrospection::unpackInfo(pba, info);
		} break;
		case PIIntrospection::itProcStat: {
			pba.clear();
			cs.get(pba);
			PIIntrospection::unpackProcStat(pba, stat);
			widgetThreads->setStat(stat.threads);
			// showInfo(info);
		} break;
		case PIIntrospection::itContainers: {
			pba.clear();
			cs.get(pba);
			PIVector<PIIntrospectionContainers::TypeInfo> data;
			PIIntrospection::unpackContainers(pba, data);
#if PIP_VERSION >= PIP_MAKE_VERSION(2, 35, 0)
			prepareContainers(data);
#endif
			widgetContainers->showContainers(data);
		} break;
		case PIIntrospection::itObjects: {
			pba.clear();
			cs.get(pba);
			PIVector<PIIntrospection::ObjectInfo> objects;
			PIIntrospection::unpackObjects(pba, objects);
			widgetObjects->showObjects(objects);
		} break;
		case PIIntrospection::itThreads: {
			pba.clear();
			cs.get(pba);
			PIVector<PIIntrospectionThreads::ThreadInfo> threads;
			PIIntrospection::unpackThreads(pba, threads);
			widgetThreads->showThreads(threads);
		} break;
		default: break;
		}
	}
	showInfo();
}


void QPIIntrospector::peersChanged(const PIString & name) {
	static QString tag = "__introspection__server_";
	listApp->blockSignals(true);
	QString cs = listApp->currentItem() ? listApp->currentItem()->text() : "";
	listApp->clear();
	peer.lock();
	for (const auto & p: peer.allPeers()) {
		QString pn = PI2QString(p.name);
		if (!pn.contains(tag)) continue;
		QListWidgetItem * li = new QListWidgetItem(pn.left(pn.indexOf(tag)));
		li->setData(Qt::UserRole, pn);
		listApp->addItem(li);
		if (pn == cs) listApp->setCurrentRow(listApp->count() - 1);
	}
	peer.unlock();
	listApp->blockSignals(false);
}


void QPIIntrospector::on_buttonRequest_clicked() {
	if (cur_server.isEmpty()) return;
	PIIntrospection::RequiredInfo info;
	for (int i = 0; i < layoutRequestFlags->count(); ++i) {
		QCheckBox * cb = qobject_cast<QCheckBox *>(layoutRequestFlags->itemAt(i)->widget());
		if (!cb) continue;
		if (!cb->isChecked()) continue;
		info.types |= cb->property("__value__").toInt();
	}
	PIByteArray ba;
	ba << PIIntrospection::sign << info;
	peer.send(cur_server, ba);
}
