#include <QScrollBar>
#include <QMessageBox>
#include <math.h>
#include "cd_pult.h"
#include "ui_cd_pult.h"
#include "piqt.h"
#include "pifile.h"
#include "qpiconfig.h"

using namespace CDUtils;


CD_Pult::CD_Pult(): QMainWindow(), config_("cd_pult.conf"),
config(piqt(config_), QIODevice::ReadWrite) {
	//cout << sizeof(coeffsK.k_protocol->to_k) << endl;
	ui = new Ui::CD_Pult();
	ui->setupUi(this);
	ui->configWidget->setQPIConfig(&config);
	ui->configWidget->expandAll();
	ui->list->viewport()->installEventFilter(this);
	ui->treeCDK->viewport()->installEventFilter(this);
	ui->scrollArea->setAutoFillBackground(false);
	ui->scrollAreaWidgetContents->setAutoFillBackground(false);
	ui->widget->setAutoFillBackground(false);
	log_menu.addAction(ui->actionClear);
	show_x = config.getValue("show_x", true);
	if (!show_x)
		ui->tabWidget->removeTab(1);
	session.setFile("session_cd_pult.conf");
	session.addEntry(this);
	session.addEntry(ui->tabWidget);
	session.addEntry(ui->checkKHideNormal);
	session.addEntry(ui->checkKHideExpressions);
	session.addEntry(ui->checkKAutoCalculate);
	session.addEntry(ui->spinHistory);
	session.addEntry(ui->spinBuffer);
	needWrite = isPause = false;
	timer = 0;
	//x.resize(KX_X_PACKET_NUM);
	//k.resize(K_NUM);
	QPalette pal = palette();
	QColor col;
	ui->graphic->setGraphicsCount(0);
	//renew();
	/*ui->treeK->setColumnWidth(0, 60);
	ui->treeK->setColumnWidth(1, 250);
	ui->treeK->setColumnWidth(3, 100);
	ui->treeK->setColumnWidth(4, 100);*/
	//ui->table->setK(coeffsK.k()->data(), coeffsK.count());
	//addToList(trUtf8("Read K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(coeffs.fileName())).arg(K.size_s()).arg(coeffs.k_content.size_s()), Qt::darkMagenta);
	/*CONNECT(void, &coeffs, sendFailed, this, pip_sendFailed);
	CONNECT(void, &coeffs, sendSucceed, this, pip_sendSucceed);
	CONNECT(void, &coeffs, receiveFailed, this, pip_receiveFailed);
	CONNECT(void, &coeffs, receiveSucceed, this, pip_receiveSucceed);*/
	CONNECTU(&K, sended, this, pip_sendSucceed);
	CONNECTU(&K, sendFailed, this, pip_sendFailed);
	CONNECTU(&K, received, this, pip_receiveSucceed);
	CONNECTU(&K, receiveFailed, this, pip_receiveFailed);
	connect(this, SIGNAL(q_k_sendFailed()), this, SLOT(k_sendFailed()), Qt::QueuedConnection);
	connect(this, SIGNAL(q_k_sendSucceed()), this, SLOT(k_sendSucceed()), Qt::QueuedConnection);
	connect(this, SIGNAL(q_k_receiveFailed()), this, SLOT(k_receiveFailed()), Qt::QueuedConnection);
	connect(this, SIGNAL(q_k_receiveSucceed()), this, SLOT(k_receiveSucceed()), Qt::QueuedConnection);
	connect(&timer_diag, SIGNAL(timeout()), this, SLOT(updateDiag()));
	connect(&session, SIGNAL(loading(QPIConfig&)), this, SLOT(loading(QPIConfig&)));
	connect(&session, SIGNAL(saving(QPIConfig&)), this, SLOT(saving(QPIConfig&)));
	connect(ui->checkKHideNormal, SIGNAL(toggled(bool)), this, SLOT(filterTree()));
	connect(ui->checkKHideExpressions, SIGNAL(toggled(bool)), this, SLOT(filterTree()));
	connect(ui->lineKSearch, SIGNAL(textChanged(QString)), this, SLOT(filterTree()));
	session.load();
	on_spinBuffer_editingFinished();
	K.reinitConnection(K.pultConfig());
	K.configure(config_);
	K.readFile();
	updateKDesc();
	updateCDesc();
	timer_diag.start(40);
}


CD_Pult::~CD_Pult() {
	session.save();
}


void CD_Pult::loading(QPIConfig & conf) {
	kdesc_file = Q2PIString(conf.getValue("kdesc_file").stringValue());
	cdesc_file = Q2PIString(conf.getValue("cdesc_file").stringValue());
}


void CD_Pult::saving(QPIConfig & conf) {
	conf.setValue("kdesc_file", PI2QString(kdesc_file));
	conf.setValue("cdesc_file", PI2QString(cdesc_file));
}


bool CD_Pult::eventFilter(QObject * o, QEvent * e) {
	if (o == ui->list->viewport()) {
		if (e->type() == QEvent::ContextMenu) {
			clear_target = 0;
			log_menu.popup(((QContextMenuEvent*)e)->globalPos());
		}
		return QMainWindow::eventFilter(o, e);
	}
	if (o == ui->treeCDK->viewport()) {
		if (e->type() == QEvent::ContextMenu) {
			clear_target = 1;
			log_menu.popup(((QContextMenuEvent*)e)->globalPos());
		}
		return QMainWindow::eventFilter(o, e);
	}
	//int ind = o->property("index").toInt();
	//qDebug() << "event" << i << e->type();
	switch (e->type()) {
	case QEvent::Enter:
		ui->graphic->setAutoUpdate(false);
		//for (int i = 0; i < KX_X_PACKET_NUM; ++i)
		//	ui->graphic->setGraphicLineWidth(ind == i ? 3. : 1., i);
		ui->graphic->setAutoUpdate(true);
		ui->graphic->update();
		break;
	case QEvent::Leave:
		ui->graphic->setAutoUpdate(false);
		//for (int i = 0; i < KX_X_PACKET_NUM; ++i)
		//	ui->graphic->setGraphicLineWidth(1., i);
		ui->graphic->setAutoUpdate(true);
		ui->graphic->update();
		break;
	default: break;
	}
	return QMainWindow::eventFilter(o, e);
}


void CD_Pult::timerEvent(QTimerEvent * e) {
	/*if (e->timerId() == timer_update) {
		if (need_update) {
			need_update = false;
			ui->graphic->updateGraphics();
		}
	}*/
	if (e->timerId() == timer) {
		static QString sPI = QString::number(atan(1) * 4., 'f', 14).leftJustified(14);
		static int cnt = 0;
		int si = qMax<int>(cnt - 6, 0);
		++cnt;
		cnt %= 23;
		ui->labelWait->setText(QString(si, QChar(' ')) + sPI.mid(cnt - 6, 6).trimmed());
		//if (!coeffs.isReady()) return;
		//ui->table->setK(coeffsK.k()->data(), coeffsK.count());
		//ui->table->showK();
	}
}


void CD_Pult::addToList(const QString & s, const QColor & c) {
	ui->list->addItem(QDateTime::currentDateTime().toString("dd/MM/yyyy  hh:ss  -  ") + s);
	ui->list->item(ui->list->count() - 1)->setTextColor(c);
	ui->list->scrollToBottom();
}


void CD_Pult::on_actionClear_triggered() {
	switch (clear_target) {
	case 0:
		ui->list->clear();
		break;
	case 1:
		clearSelected();
		break;
	default: break;
	}
}


void CD_Pult::clearSelected() {
	/*QList<QTreeWidgetItem * > si = ui->treeK->selectedItems();
	ui->treeK->setUpdatesEnabled(false);
	ui->treeK->blockSignals(true);
	foreach (QTreeWidgetItem * i, si) {
//		int ki = i->text(0).toInt();
		i->setText(2, "");
		//coeffs.setFormula(ki, "");
	}
	ui->treeK->blockSignals(false);
	ui->treeK->setUpdatesEnabled(true);
	if (ui->checkKAutoCalculate->isChecked()) {
		QApplication::processEvents();
		//calculate();
	}*/
}


QString CD_Pult::typeName(const QString & n) const {
	if (n.isEmpty()) return "";
	switch (n[0].toLatin1()) {
	case 'l': return trUtf8("list"); break;
	case 'b': return trUtf8("bool"); break;
	case 'n': return trUtf8("int"); break;
	case 'f': return trUtf8("double"); break;
	case 'c': return trUtf8("color"); break;
	case 'r': return trUtf8("rect"); break;
	case 'a': return trUtf8("rect"); break;
	case 'p': return trUtf8("point"); break;
	case 'v': return trUtf8("vector"); break;
	case 'i': return trUtf8("IP"); break;
	case 'e': return trUtf8("enum"); break;
	case 'F': return trUtf8("file"); break;
	case 'D': return trUtf8("dir"); break;
	}
	return trUtf8("double");
}


void CD_Pult::makeTreeSection(CDSection & ks, QTreeWidgetItem * pi) {
	PIMap<int, CDType>::iterator ki;
	for (ki = ks.k.begin(); ki != ks.k.end(); ++ki) {
		QTreeWidgetItem * ti = new QTreeWidgetItem(pi);
		const CDType & ck(ki.value());
		ti->setText(0, QString::number(ck.index()));
		ti->setText(1, PI2QString(ck.name()));
		ti->setText(2, typeName(PI2QString(ck.type())));
		ti->setText(3, PI2QString(ck.formula()));
		ti->setText(5, PI2QString(ck.comment()));
	}
	PIMap<int, CDSection>::iterator si;
	for (si = ks.s.begin(); si != ks.s.end(); ++si) {
		QTreeWidgetItem * ti = new QTreeWidgetItem(pi);
		CDSection & cs(si.value());
		ti->setText(0, QString("[%1]").arg(si.key()));
		ti->setText(1, PI2QString(cs.alias));
		ti->setText(2, PI2QString(cs.name));
		makeTreeSection(cs, ti);
	}
}


void CD_Pult::on_treeK_itemClicked(QTreeWidgetItem * item, int column) {
	Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	if (column == 2) f |= Qt::ItemIsEditable;
	item->setFlags(f);
}


void CD_Pult::on_treeK_itemChanged(QTreeWidgetItem * item, int column) {
	if (column != 2) return;
//	int ki = item->text(0).toInt();
	//coeffs.setFormula(ki, piqt(item->text(column)));
	if (ui->checkKAutoCalculate->isChecked())
		;//calculate();
}


void CD_Pult::on_buttonSendK_clicked() {
//	on_buttonWrite_clicked();
	K.send();
	//coeffs.sendCoeffs();
	if (timer != 0) killTimer(timer);
	timer = startTimer(100);
}


void CD_Pult::on_buttonReceiveK_clicked() {
	K.request();
//	K.se
	//coeffs.receiveCoeffs();
	if (timer != 0) killTimer(timer);
	timer = startTimer(100);
}


void CD_Pult::on_buttonShowAll_clicked() {
	/*for (int i = 0; i < KX_X_PACKET_NUM; ++i) {
		checks[i]->check.setChecked(true);
		//ui->graphic->setGraphicVisible(true, i);
	}*/
}


void CD_Pult::on_buttonHideAll_clicked() {
	/*for (int i = 0; i < KX_X_PACKET_NUM; ++i) {
		checks[i]->check.setChecked(false);
		//ui->graphic->setGraphicVisible(false, i);
	}*/
}


void CD_Pult::on_buttonRead_clicked() {
	K.readFile();
	addToList(trUtf8("Read K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(K.file())).arg(K.count()).arg(K.fileSize()), Qt::darkMagenta);
	updateTree();
}


void CD_Pult::on_buttonWrite_clicked() {
	K.writeFile();
	addToList(trUtf8("Write K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(K.file())).arg(K.count()).arg(K.fileSize()), Qt::darkMagenta);
}


void CD_Pult::on_buttonSetKDesc_clicked() {
	QString ret = QFileDialog::getOpenFileName(this, trUtf8("Select *.h file with K description"), PI2QString(kdesc_file), "C/C++ header files(*.h *.hpp);;All files(*)");
	if (ret.isEmpty()) return;
	kdesc_file = Q2PIString(QDir::current().relativeFilePath(ret));
	PIFile f(kdesc_file, PIIODevice::ReadOnly);
	piCout << "open description:" << Q2PIString(ret);
	//K.update(&f);
	updateKDesc(true);
}


void CD_Pult::on_buttonSetCDesc_clicked() {
	QString ret = QFileDialog::getOpenFileName(this, trUtf8("Select *.h file with C description"), PI2QString(cdesc_file), "C/C++ header files(*.h *.hpp);;All files(*)");
	if (ret.isEmpty()) return;
	cdesc_file = Q2PIString(QDir::current().relativeFilePath(ret));
	updateCDesc();
}


void CD_Pult::k_sendFailed() {
	addToList(trUtf8("K not sended"), Qt::darkRed);
	if (timer != 0) killTimer(timer);
}


void CD_Pult::k_sendSucceed() {
	addToList(trUtf8("K sended"), Qt::darkGreen);;
	if (timer != 0) killTimer(timer);
}


void CD_Pult::k_receiveFailed() {
	addToList(trUtf8("K not received"), Qt::darkRed);
	if (timer != 0) killTimer(timer);
}


void CD_Pult::k_receiveSucceed() {
	addToList(trUtf8("K received"), Qt::darkGreen);
	if (timer != 0) killTimer(timer);
	updateTree();
}


void CD_Pult::on_spinBuffer_editingFinished() {
	ui->graphic->setHistorySize(ui->spinBuffer->value());
}


void CD_Pult::updateGraph() {
	ui->graphic->updateGraphics();
}


void CD_Pult::updateDiag() {
	/*ui->labelKReceiver->setText(piqt(coeffs.k_protocol->receiverDeviceName() + " - " + coeffs.k_protocol->receiverDeviceState()));
	ui->labelKSender->setText(piqt(coeffs.k_protocol->senderDeviceName()));
	ui->spinKSended->setValue(coeffs.k_protocol->sendCount());
	ui->spinKReceived->setValue(coeffs.k_protocol->receiveCount());
	ui->spinKWrong->setValue(coeffs.k_protocol->wrongCount());
	ui->spinKMissed->setValue(coeffs.k_protocol->missedCount());
	ui->labelKType->setText("0x" + QString::number(coeffs.k_protocol->from_k.type, 16).toUpper().rightJustified(2, '0'));
	ui->labelKAddrPult->setText("0x" + QString::number(coeffs.k_protocol->from_k.addr_to, 16).toUpper().rightJustified(2, '0'));
	ui->labelKAddr->setText("0x" + QString::number(coeffs.k_protocol->to_k.addr_to, 16).toUpper().rightJustified(2, '0'));
	
	ui->labelXReceiver->setText(piqt(prot_x->receiverDeviceName() + " - " + prot_x->receiverDeviceState()));
	ui->labelXSender->setText(piqt(prot_x->senderDeviceName()));
	ui->spinXSended->setValue(prot_x->sendCount());
	ui->spinXReceived->setValue(prot_x->receiveCount());
	ui->spinXWrong->setValue(prot_x->wrongCount());
	ui->spinXMissed->setValue(prot_x->missedCount());
	ui->labelXType->setText("0x" + QString::number(prot_x->from_x.type, 16).toUpper().rightJustified(2, '0'));
	ui->labelXAddrPult->setText("0x" + QString::number(prot_x->from_x.addr_to, 16).toUpper().rightJustified(2, '0'));
	ui->labelXAddr->setText("0x" + QString::number(prot_x->to_x.addr_to, 16).toUpper().rightJustified(2, '0'));

	ui->labelCReceiver->setText(piqt(prot_c->receiverDeviceName() + " - " + prot_c->receiverDeviceState()));
	ui->labelCSender->setText(piqt(prot_c->senderDeviceName()));
	ui->spinCSended->setValue(prot_c->sendCount());
	ui->spinCReceived->setValue(prot_c->receiveCount());
	ui->spinCWrong->setValue(prot_c->wrongCount());
	ui->spinCMissed->setValue(prot_c->missedCount());
	ui->labelCType->setText("0x" + QString::number(prot_c->from_c.type, 16).toUpper().rightJustified(2, '0'));
	ui->labelCAddrPult->setText("0x" + QString::number(prot_c->from_c.addr_to, 16).toUpper().rightJustified(2, '0'));
	ui->labelCAddr->setText("0x" + QString::number(prot_c->to_c.addr_to, 16).toUpper().rightJustified(2, '0'));
	*/
}


void CD_Pult::updateKDesc(bool ask_move) {
	addToList(trUtf8("Update K description file \"%1\"").arg(PI2QString(kdesc_file)), Qt::darkMagenta);
	PIFile f(kdesc_file, PIIODevice::ReadOnly);
	//piCout << "call K.update():";
	K.root() = CDSection();
	K.update(&f);
	updateTree(ask_move);
}


void CD_Pult::updateCDesc() {
	//parseHeader(cdesc_file, cdesc);
	updateCommands();
}


bool stringComp(const QString & s1, const QString & s2) {
	if (s1.size() != s2.size())
		return s1.size() > s2.size();
	return s1 > s2;
}


void CD_Pult::updateTree(bool move) {
}


void CD_Pult::updateCommands() {
	while (ui->layoutCommands->count() > 0)
		delete ui->layoutCommands->itemAt(0)->widget();
	/*QMapIterator<int, KDesc> it(cdesc);
	while (it.hasNext()) {
		it.next();
		KDesc kd = it.value();
		QPushButton * b = new QPushButton();
		QString text = kd.name;
		if (!kd.comment.isEmpty())
			text += QString("\n(%1)").arg(kd.comment);
		b->setText(text);
		b->setProperty("_command", kd.index);
		connect(b, SIGNAL(clicked()), this, SLOT(commandClicked()));
		ui->layoutCommands->addWidget(b);
	}*/
}


void CD_Pult::filterTree() {
	/*bool hn = ui->checkKHideNormal->isChecked();
	bool hs = ui->checkKHideExpressions->isChecked();
	bool ok = false;
	QString fl = ui->lineKSearch->text();
	int lc = ui->treeK->topLevelItemCount();
	for (int i = 0; i < lc; ++i) {
		QTreeWidgetItem * ti = ui->treeK->topLevelItem(i);
		if (fl.isEmpty())
			ti->setHidden(false);
		else
			ti->setHidden(!ti->text(0).contains(fl, Qt::CaseInsensitive) &&
						  !ti->text(1).contains(fl, Qt::CaseInsensitive) &&
						  !ti->text(2).contains(fl, Qt::CaseInsensitive) &&
						  !ti->text(3).contains(fl, Qt::CaseInsensitive) &&
						  !ti->text(4).contains(fl, Qt::CaseInsensitive));
		if (hn)
			if (ti->data(0, Qt::UserRole).toBool())
				ti->setHidden(true);
		if (hs) {
			ti->data(2, Qt::DisplayRole).toDouble(&ok);
			if (!ok)
				ti->setHidden(true);
		}
	}*/
}


void CD_Pult::markError(QTreeWidgetItem * item, const QString & tool_tip) {
	int cc = item->columnCount();
	for (int i = 0; i < cc; ++i)
		item->setBackgroundColor(i, QColor(255, 200, 200));
	if (item->toolTip(2).isEmpty())
		item->setToolTip(2, tool_tip);
	if (item->toolTip(3).isEmpty())
		item->setToolTip(3, tool_tip);
	item->setData(0, Qt::UserRole, false);
	item->setText(3, "Error");
}


void CD_Pult::markNormal(QTreeWidgetItem * item) {
	int cc = item->columnCount();
	for (int i = 0; i < cc; ++i)
		item->setBackground(i, Qt::NoBrush);
	item->setToolTip(2, QString());
	item->setToolTip(3, QString());
	item->setData(0, Qt::UserRole, true);
}


void CD_Pult::progress(int val, int max) {
}


void CD_Pult::commandClicked() {
	QPushButton * b = qobject_cast<QPushButton*>(sender());
	if (!b) return;
	//prot_c->sendCommand(b->property("_command").toInt());
}
