#include "cdgraphics.h"
//#include "ui_qcd_graphic.h"
#include "cdutils_core.h"
#include "cdutils_x.h"
#include "qcd_core.h"
#include "qcd_model.h"
#include "qcd_graphic.h"
#include "graphic.h"
#include "piqt.h"
#include <QMimeData>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMainWindow>
#include <QDockWidget>
#include <QInputDialog>

using namespace CDUtils;


QStringList CDUtils::getList(const PIVector<PIDeque<int> > & x_list) {
	QStringList ret;
	piForeachC (PIDeque<int> & p, x_list)
		ret << PI2QString(CDCore::pathToString(p));
	return ret;
}


PIVector<PIDeque<int> > CDUtils::setList(const QStringList & l) {
	PIVector<PIDeque<int> > ret;
	foreach (QString s, l)
		ret << CDCore::stringToPath(Q2PIString(s));
	return ret;
}




GDockWidget::GDockWidget(QString title, QMainWindow * p): QDockWidget(title, p) {
	da = p;
	menu = new QMenu(this);
	QAction * a = new QAction(QIcon(":/icons/document-edit.png"), "Rename ...", this);
	connect(a, SIGNAL(triggered(bool)), this, SLOT(rename()));
	dactions << a;
	a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove", this);
	connect(a, SIGNAL(triggered(bool)), this, SIGNAL(removeRequest()));
	dactions << a;
	menu_x = new QMenu(this);
	menu_x->setTitle(tr("Remove X"));
	graphic = new CDGraphicWidget();
	graphic->graphic()->viewport()->setAcceptDrops(true);
	graphic->graphic()->viewport()->installEventFilter(this);
	setWidget(graphic);
}


void GDockWidget::addX(const CDType & t) {
	if (t.cd_type() != CDType::cdX) return;
	PIDeque<int> xp = t.path();
	if (x_list.contains(xp)) return;
	x_list << xp;
	int gind = graphic->graphic()->graphicsCount();
	graphic->graphic()->setGraphicsCount(gind + 1);
	graphic->graphic()->setGraphicName(PI2QString(t.pathString().join(".")), gind);
}


void GDockWidget::drawX(const PIMap<PIString, PIVector<double> > & data) {
	for (int i = 0; i < x_list.size_s(); ++i) {
		PIString sp = CDCore::pathToString(x_list[i]);
		const PIVector<double> & ch(data[sp]);
		for (int j = 0; j < ch.size_s(); ++j)
			graphic->graphic()->addPoint(ch[j], i, false);
	}
	graphic->graphic()->updateGraphics();
}


QByteArray GDockWidget::save() const {
	ChunkStream cs;
	cs.add(1, windowTitle())
	  .add(2, getList(x_list))
	  .add(3, graphic->graphic()->save())
	  .add(4, graphic->evalSpinBoxHistory()->expression())
	  .add(5, graphic->evalSpinBoxVisible()->expression());
	return cs.data();
}


void GDockWidget::load(QByteArray ba) {
	if (ba.isEmpty()) return;
	ChunkStream cs(ba);
	while (!cs.atEnd()) {
		switch (cs.read()) {
		case 1: setWindowTitle(cs.getData<QString>()); break;
		case 2: x_list = setList(cs.getData<QStringList>()); break;
		case 3: graphic->graphic()->load(cs.getData<QByteArray>()); break;
		case 4: graphic->evalSpinBoxHistory()->setExpression(cs.getData<QString>()); break;
		case 5: graphic->evalSpinBoxVisible()->setExpression(cs.getData<QString>()); break;
		default: break;
		}
	}
}


void GDockWidget::changedGlobal() {
	for (int i = 0; i < x_list.size_s(); ++i) {
		if (!X.exists(x_list[i])) {
			x_list.remove(i);
			graphic->graphic()->removeGraphic(i);
			--i;
			continue;
		}
		graphic->graphic()->setGraphicName(PI2QString(X[x_list[i]].pathString().join(".")), i);
	}
}


bool GDockWidget::eventFilter(QObject * o, QEvent * e) {
	//if (o == graphic->viewport()) {
		switch (e->type()) {
		case QEvent::DragMove: {
			QDragMoveEvent * de = (QDragMoveEvent*)e;
			const QMimeData * mime = de->mimeData();
			//qDebug() << "enter" << mime;
			if (!mime) break;
			if (!mime->text().startsWith("x")) break;
			de->setDropAction(Qt::CopyAction);
			de->accept();
			return true;
		} break;
		case QEvent::DragEnter: {
			QDragEnterEvent * de = (QDragEnterEvent*)e;
			const QMimeData * mime = de->mimeData();
			//qDebug() << "enter" << mime;
			if (!mime) break;
			if (!mime->text().startsWith("x")) break;
			de->setDropAction(Qt::CopyAction);
			de->accept();
			return true;
			} break;
		case QEvent::Drop: {
			QDropEvent * de = (QDropEvent*)e;
			const QMimeData * mime = de->mimeData();
			if (!mime) break;
			//qDebug() << "drop" << mime->text();
			if (!mime->text().startsWith("x")) break;
			addX(X[CDCore::stringToPath(Q2PIString(mime->text().mid(1)))]);
			de->accept();
			return true;
		} break;
		default: break;
		}
	//}
	return QWidget::eventFilter(o, e);
}


void GDockWidget::contextMenuEvent(QContextMenuEvent * e) {
	if (graphic->graphic()->underMouse()) return;
	qDeleteAll(menu_x->actions());
	menu_x->clear();
	for (int i = 0; i < graphic->graphic()->graphicsCount(); ++i) {
		QPixmap icon(da->iconSize());
		icon.fill(graphic->graphic()->graphic(i).pen.color());
		QAction * a = new QAction(QIcon(icon), graphic->graphic()->graphic(i).name, this);
		a->setData(i);
		connect(a, SIGNAL(triggered(bool)), this, SLOT(removeX()));
		menu_x->addAction(a);
	}
	QMenu * mwm = da->createPopupMenu();
	menu->clear();
	menu->addActions(dactions);
	menu->addMenu(menu_x);
	menu->addSeparator();
	menu->addActions(mwm->actions());
	menu->popup(e->globalPos());
	mwm->deleteLater();
}


CDGraphicWidget * GDockWidget::viewportGraphic(QObject * o) const {
	if (!o) return 0;
	while (!qobject_cast<CDGraphicWidget*>(o) && o)
		o = o->parent();
	return qobject_cast<CDGraphicWidget*>(o);
}


void GDockWidget::rename() {
	QString nn = QInputDialog::getText(this, tr("Rename area"), tr("New area name:"),
									   QLineEdit::Normal, windowTitle());
	if (nn.isEmpty()) return;
	setWindowTitle(nn);
}


void GDockWidget::removeX() {
	QAction * a = qobject_cast<QAction * >(sender());
	if (!a) return;
	int ind = a->data().toInt();
	if (ind < 0 || ind >= x_list.size_s()) return;
	x_list.remove(ind);
	graphic->graphic()->removeGraphic(ind);
}




CDGraphics::CDGraphics(QWidget * parent) : QWidget(parent), Ui::CDGraphics() {
	setupUi(this);
	da = new QMainWindow();
	da->setWindowFlags(frame->windowFlags());
	da->setDockNestingEnabled(true);
	layoutMain->addWidget(da);
}


CDGraphics::~CDGraphics() {
}


void CDGraphics::reset() {
	qDeleteAll(docks);
	docks.clear();
}


QByteArray CDGraphics::save() const {
	ChunkStream cs;
	QVector<QByteArray> dstates;
	foreach (GDockWidget * d, docks) {
		dstates << d->save();
	}
	cs.add(1, docks.size())
	  .add(2, dstates)
	  .add(3, da->saveState());
	X.lock();
	cs.add(4, getList(X.enabledList()));
	X.unlock();
	cs.add(5, buttonConfigVisible->isChecked());
	return cs.data();
}


void CDGraphics::load(QByteArray ba) {
	reset();
	if (ba.isEmpty()) return;
	ChunkStream cs(ba);
	while (!cs.atEnd()) {
		switch (cs.read()) {
		case 1: {
			int s = cs.getData<int>();
			piForTimes (s)
				addGraphic();
			} break;
		case 2: {
			QVector<QByteArray> dstates = cs.getData<QVector<QByteArray> >();
			for (int i = 0; i < piMini(dstates.size(), docks.size()); ++i)
				docks[i]->load(dstates[i]);
			} break;
		case 3: da->restoreState(cs.getData<QByteArray>()); break;
		case 4:
			X.lock();
			X.setEnabledList(setList(cs.getData<QStringList>()));
			X.unlock();
			break;
		case 5:
			buttonConfigVisible->setChecked(cs.getData<bool>());
			break;
		default: break;
		}
	}
}


GDockWidget * CDGraphics::graphicDock(Graphic * o) const {
	if (!o) return 0;
	foreach (GDockWidget * d, docks)
		if (d->widget() == o)
			return d;
	return 0;
}


void CDGraphics::addGraphic() {
	GDockWidget * dw = new GDockWidget(QString("area %1").arg(docks.size()), da);
	connect(dw, SIGNAL(removeRequest()), this, SLOT(removeGraphic()));
	connect(buttonConfigVisible, SIGNAL(toggled(bool)), dw->graphic, SLOT(setConfigVisible(bool)));
	connect(buttonLegendVisible, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setLegendVisible(bool)));
	connect(buttonBorderInputsVisible, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setBorderInputsVisible(bool)));
	connect(buttonPause, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setPaused(bool)));
	dw->graphic->setConfigVisible(buttonConfigVisible->isChecked());
	dw->graphic->graphic()->setLegendVisible(buttonLegendVisible->isChecked());
	dw->graphic->graphic()->setBorderInputsVisible(buttonBorderInputsVisible->isChecked());
	da->addDockWidget(Qt::RightDockWidgetArea, dw);
	docks << dw;
	for (int i = 0; i < docks.size(); ++i)
		docks[i]->setObjectName(QString("dock_%1").arg(i));
}


void CDGraphics::receivedX() {
	PIMap<PIString, PIVector<double> > data;
	X.lock();
	PIVector<PIDeque<int> > x_list = X.enabledList();
	PIVector<double> ch;
	piForeachC (PIDeque<int> & p, x_list) {
		CDType & t(X[p]);
		if (t.xmode_rec() == CDType::X_Current)
			ch.resize(1).fill(t.toDouble());
		else
			ch = t.history;
		t.history.clear();
		data[CDCore::pathToString(t.path())] = ch;
	}
	//piCout << data;
	X.unlock();
	foreach (GDockWidget * d, docks)
		d->drawX(data);
}


void CDGraphics::changedGlobal() {
	foreach (GDockWidget * d, docks)
		d->changedGlobal();
}


void CDGraphics::removeGraphic() {
	GDockWidget * d = qobject_cast<GDockWidget * >(sender());
	if (!d) return;
	docks.removeAll(d);
	d->deleteLater();
	for (int i = 0; i < docks.size(); ++i)
		docks[i]->setObjectName(QString("dock_%1").arg(i));
}


void CDGraphics::on_buttonAdd_clicked() {
	addGraphic();
}


void CDGraphics::on_buttonClear_clicked() {
	foreach (GDockWidget * d, docks)
		d->graphic->graphic()->clear();
}


void CDGraphics::on_buttonRemoveAll_clicked() {
	qDeleteAll(docks);
	docks.clear();
}
