#include <QDir>
#include <QMouseEvent>
#include <QSortFilterProxyModel>
#include "cdutils_k.h"
#include "cdutils_x.h"
#include "cdutils_c.h"
#include "cdutils_m.h"
#include "cdutils_core.h"
#include "qcd_view.h"
#include "qcd_model.h"
#include "piqt.h"
#include "pifile.h"

using namespace CDUtils;


CDView::CDView(QWidget * parent) : QTreeView(parent) {
	type_ = -1;
	model_ = 0;
	proxy_ = 0;
	connect(this, SIGNAL(clicked(QModelIndex)),  this, SLOT(indexClicked(QModelIndex)));
	connect(this, SIGNAL(_qcd_sendFailed()),     this, SLOT(cd_sendFailed()),     Qt::QueuedConnection);
	connect(this, SIGNAL(_qcd_sendSucceed()),    this, SLOT(cd_sendSucceed()),    Qt::QueuedConnection);
	connect(this, SIGNAL(_qcd_receiveFailed()),  this, SLOT(cd_receiveFailed()),  Qt::QueuedConnection);
	connect(this, SIGNAL(_qcd_receiveSucceed()), this, SLOT(cd_receiveSucceed()), Qt::QueuedConnection);
	connect(this, SIGNAL(_qcd_receivedX()),      this, SLOT(cd_receivedX()),      Qt::QueuedConnection);
	connect(this, SIGNAL(_qcd_changedGlobal()),  this, SLOT(cd_changedGlobal()),  Qt::QueuedConnection);
}


CDView::~CDView() {
	if (model_) {
		delete model_;
		delete proxy_;
	}
	model_ = 0;
	proxy_ = 0;
}


void CDView::setType(int cdt) {
	if (cdt < 0) return;
	if (type_ >= 0) return;
	type_ = cdt;
	switch ((CDType::cdT)type_) {
	case CDType::cdK:
		CONNECTU(&K, sended,        this, pi_cd_sendSucceed);
		CONNECTU(&K, sendFailed,    this, pi_cd_sendFailed);
		CONNECTU(&K, received,      this, pi_cd_receiveSucceed);
		CONNECTU(&K, receiveFailed, this, pi_cd_receiveFailed);
		CONNECTU(&K, changedGlobal, this, pi_cd_changedGlobal);
		break;
	case CDType::cdX:
		CONNECTU(&X, sended,        this, pi_cd_sendSucceed);
		CONNECTU(&X, sendFailed,    this, pi_cd_sendFailed);
		CONNECTU(&X, received,      this, pi_cd_receiveSucceed);
		CONNECTU(&X, receiveFailed, this, pi_cd_receiveFailed);
		CONNECTU(&X, receivedX,     this, pi_cd_receivedX);
		CONNECTU(&X, changedGlobal, this, pi_cd_changedGlobal);
		break;
	case CDType::cdC:
		CONNECTU(&C, sended,        this, pi_cd_sendSucceed);
		CONNECTU(&C, sendFailed,    this, pi_cd_sendFailed);
		CONNECTU(&C, received,      this, pi_cd_receiveSucceed);
		CONNECTU(&C, receiveFailed, this, pi_cd_receiveFailed);
		CONNECTU(&C, changedGlobal, this, pi_cd_changedGlobal);
		break;
	case CDType::cdM:
		CONNECTU(&M, sended,        this, pi_cd_sendSucceed);
		CONNECTU(&M, sendFailed,    this, pi_cd_sendFailed);
		CONNECTU(&M, received,      this, pi_cd_receiveSucceed);
		CONNECTU(&M, receiveFailed, this, pi_cd_receiveFailed);
		CONNECTU(&M, changedGlobal, this, pi_cd_changedGlobal);
		CONNECTU(&M, messageReceived, this, pi_cd_messageReceived);
		break;
	default: break;
	}

}


void CDView::mousePressEvent(QMouseEvent * e) {
	if (type_ == CDType::cdC) {
		QModelIndex i = indexAt(e->pos());
		if (i.isValid() && i.column() == cName_Cmd)
			update(i);
	}
	QTreeView::mousePressEvent(e);
}


void CDView::mouseReleaseEvent(QMouseEvent * e) {
	if (type_ == CDType::cdC) {
		QModelIndex i = indexAt(e->pos());
		if (i.isValid() && i.column() == cName_Cmd)
			update(i);
	}
	QTreeView::mouseReleaseEvent(e);
}


void CDView::currentChanged(const QModelIndex & cur, const QModelIndex & prev) {
	if (type_ == CDType::cdC) {
		if (prev.isValid() && prev.column() == cName_Cmd)
			update(prev);
	}
	QTreeView::currentChanged(cur, prev);
}


void CDView::refresh() {
	if (type_ < 0) return;
	if (!model_) {
		model_ = new CDItemModel(type_);
		proxy_ = new QSortFilterProxyModel();
		proxy_->setRecursiveFilteringEnabled(true);
		proxy_->setFilterKeyColumn(-1);
		proxy_->setFilterCaseSensitivity(Qt::CaseInsensitive);
		proxy_->setSourceModel(model_);
		setModel(proxy_);
		setItemDelegateForColumn(type_ == CDType::cdC ? cName_Cmd : cValue, new CDDelegate());
		if (type_ == CDType::cdX)
			setItemDelegateForColumn(cXMode, new CDDelegate());
	}
	model_->rebuildModel();
	switch ((CDType::cdT)type_) {
	case CDType::cdK:
		setColumnHidden(cXMode,      true);
		setColumnHidden(cXAvg,       true);
		break;
	case CDType::cdX:
		setColumnHidden(cExpression, true);
		break;
	case CDType::cdC:
	case CDType::cdM:
		setColumnHidden(cType,       true);
		setColumnHidden(cXMode,      true);
		setColumnHidden(cXAvg,       true);
		setColumnHidden(cExpression, true);
		setColumnHidden(cValue,      true);
		break;
	default: break;
	}
	expandAll();
	for (int i = 0; i < model_->columnCount(); i++) resizeColumnToContents(i);
}


void CDView::refreshValues() {
	if (!model_) return;
	model_->dataChanged(model_->index(0, 0), model_->index(model_->columnCount() - 1, model_->rowCount() - 1));
}


void CDView::setFile(const QString & filename) {
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.setFileName(Q2PIString(filename)); break;
	case CDType::cdX: X.setFileName(Q2PIString(filename)); break;
	case CDType::cdC: C.setFileName(Q2PIString(filename)); break;
	case CDType::cdM: M.setFileName(Q2PIString(filename)); break;
	default: break;
	}
}


bool CDView::inProgress() const {
	switch ((CDType::cdT)type_) {
	case CDType::cdK: return K.inProgress(); break;
	case CDType::cdX: return X.inProgress(); break;
	case CDType::cdC: return C.inProgress(); break;
	case CDType::cdM: return M.inProgress(); break;
	default: break;
	}
	return false;
}


void CDView::startX(double freq) {
	switch ((CDType::cdT)type_) {
	case CDType::cdX: X.start(freq); break;
	default: break;
	}
}


CDSection * CDView::root() {
	return CDCore::instance()->root((CDType::cdT)type_);
}


QString CDView::typeLetter() const {
	return PI2QString(CDCore::instance()->typeLetter((CDType::cdT)type_));
}


void CDView::send() {
	busyStatusChanged(true);
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.send(); break;
	case CDType::cdX: X.send(); break;
	case CDType::cdC: C.send(); break;
	case CDType::cdM: M.send(); break;
	default: break;
	}
}


void CDView::receive() {
	busyStatusChanged(true);
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.request(); break;
	case CDType::cdX: X.request(); break;
	case CDType::cdC: C.request(); break;
	case CDType::cdM: M.request(); break;
	default: break;
	}
}


void CDView::save() {
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.writeFile(); break;
	case CDType::cdX: X.writeFile(); break;
	case CDType::cdC: C.writeFile(); break;
	case CDType::cdM: M.writeFile(); break;
	default: break;
	}
}


void CDView::load() {
	switch ((CDType::cdT)type_) {
	case CDType::cdK:
		K.readFile();
		K.calculate();
		break;
	case CDType::cdX:
		X.readFile();
		X.calculate();
		break;
	case CDType::cdC:
		C.readFile();
		C.calculate();
		break;
	case CDType::cdM:
		M.readFile();
		M.calculate();
		break;
	default: break;
	}
	refresh();
}


void CDView::clear() {
	//piCout << "clearK";
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.root() = CDSection(); break;
	case CDType::cdX: X.root() = CDSection(); break;
	case CDType::cdC: C.root() = CDSection(); break;
	case CDType::cdM: M.root() = CDSection(); break;
	default: break;
	}
	refresh();
}


void CDView::buildFromHeader(const QString & description, int mode) {
	if (description.isEmpty()) return;
	PIString desc_file = Q2PIString(QDir::current().relativeFilePath(description));
	PIFile f(desc_file, PIIODevice::ReadOnly);
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.update(&f, mode); break;
	case CDType::cdX: X.update(&f, mode); break;
	case CDType::cdC: C.update(&f, mode); break;
	case CDType::cdM: M.update(&f, mode); break;
	default: break;
	}
	refresh();
}


void CDView::calculate() {
	switch ((CDType::cdT)type_) {
	case CDType::cdK: K.calculate(); break;
	case CDType::cdX: X.calculate(); break;
	case CDType::cdC: C.calculate(); break;
	case CDType::cdM: M.calculate(); break;
	default: break;
	}
}


void CDView::filter(const QString & f) {
	proxy_->setFilterRegExp(QRegExp(f, Qt::CaseInsensitive));
}


void CDView::indexClicked(const QModelIndex & i) {
	if (!model_ || !i.isValid() || type_ != CDType::cdC || i.column() != cName_Cmd) return;
	CDItem * item = model_->getItem(i);
	if (!item) return;
	if (item->itemType() != CDItem::ItemCDType) return;
	CDType & t(model_->interface->section(item->buildPath())[item->index()]);
	C.sendCommand(t);
	emit commandSended(PI2QString(t.pathString().join(".")));
	//piCout << t;
	qDebug() << PI2QString(t.pathString().join("."));
}


void CDView::cd_sendFailed() {
	busyStatusChanged(false);
	emit messageStatus("send failed");
	emit sendFailed();
}


void CDView::cd_sendSucceed() {
	busyStatusChanged(false);
	emit messageStatus("send success");
	emit sendSucceed();
}


void CDView::cd_receiveFailed() {
	busyStatusChanged(false);
	emit messageStatus("receive failed");
	emit receiveFailed();
}


void CDView::cd_receiveSucceed() {
	refresh();
	busyStatusChanged(false);
	emit messageStatus("receive success");
	emit receiveSucceed();
}


void CDView::cd_receivedX() {
	X.lock();
	PIVector<PIDeque<int> > xl = X.enabledList();
	//piCout << "X" << xl.size();
	piForeachC (PIDeque<int> & x, xl) {
		CDType & t(X[x]);
		//piCout << t;
		//piCout << t.path();
		if (t.cd_type() != CDType::cdX) continue;
		update(model_->indexByPath(t.path(), cValue));
		//piCout << CDCore::pathToString(t.path()) << t.toDouble() << "model";
		//qDebug() << "val" << model_->data(model_->indexByPath(t.path(), cValue), Qt::DisplayRole).toDouble();
	}
	X.unlock();
	emit receivedX();
}


void CDView::cd_changedGlobal() {
	emit changedGlobal();
}


void CDView::pi_cd_messageReceived(PIDeque<int> path, int type, PIString msg) {
	QMetaObject::invokeMethod(this, "messageReceived", Qt::QueuedConnection,
							  Q_ARG(QString, PI2QString(CDCore::pathToString(path))),
							  Q_ARG(int, type),
							  Q_ARG(QString, PI2QString(msg)));
}
