#include "object_tree.h"
#include "ui_object_tree.h"
#include "SH_runtime.h"

const int DataType  = Qt::UserRole + 1;
const int DataFlags = Qt::UserRole + 2;
const int DataPath  = Qt::UserRole + 3;
const int DataCmd   = Qt::UserRole + 4;

enum ItemType {
	itObject = 1,
	itProperty,
	itPacket,
};

Q_DECLARE_METATYPE(SHS::Command);

ObjectTree::ObjectTree(QWidget * parent): QWidget(parent) {
	ui = new Ui::ObjectTree();
	ui->setupUi(this);
	qRegisterMetaType<SHS::Command>();
	icon_signal   = QIcon(":/icons/code-signal.png");
	icon_command  = QIcon(":/icons/code-function.png");
	icon_property = QIcon(":/icons/code-variable.png");
	icon_object   = QIcon(":/icons/code-struct.png");
	ui->tree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
	ui->tree->addActions(QList<QAction*>() << ui->actionExpand << ui->actionCollapse);
}


void ObjectTree::clear() {
	ui->tree->clear();
}


void ObjectTree::create(const SHS::Object & root) {
	clear();
	createObjectsTree(ui->tree->invisibleRootItem(), root);
	ui->tree->expandAll();
	filterBlocks();
}


void ObjectTree::setFilterButtonsVisible(bool yes) {
	ui->buttonShowBlocksProperties->setVisible(yes);
	ui->buttonShowBlocksEvents    ->setVisible(yes);
	ui->buttonShowBlocksCommands  ->setVisible(yes);
}


void ObjectTree::setPropertiesVisible(bool yes) {
	ui->buttonShowBlocksProperties->setChecked(yes);
}


void ObjectTree::setEventsVisible(bool yes) {
	ui->buttonShowBlocksEvents->setChecked(yes);
}


void ObjectTree::setCommandsVisible(bool yes) {
	ui->buttonShowBlocksCommands->setChecked(yes);
}


void ObjectTree::addBlockPacketItem(const SHS::Command & c, const QString & name, QTreeWidgetItem * ni, SHS::PacketFlag flag, const QIcon & icon) {
	if (!c.flags.testFlag(flag)) return;
	QTreeWidgetItem * si = new QTreeWidgetItem(ni);
	QString tooltip = c.desc;
	if (!tooltip.isEmpty()) tooltip.append("\n");
	//tooltip.append(r.value("command").toString());
	si->setToolTip(0, tooltip);
	si->setData(0, DataType, int(itPacket));
	si->setData(0, DataFlags, int(flag));
	si->setData(0, DataCmd, QVariant::fromValue(c));
	si->setText(0, QString("%1(%2)").arg(name, c.user_arguments.join(", ")));
	si->setIcon(0, icon);
	si->setFirstColumnSpanned(true);
}


void ObjectTree::addBlockPropertyItem(const QString & p, const QString & v, const QStringList & obj_path, QTreeWidgetItem * ni, const QIcon & icon) {
	//if (!isCName(p)) return;
	QTreeWidgetItem * si = new QTreeWidgetItem(ni);
	si->setData(0, DataType, int(itProperty));
	si->setData(0, DataPath, obj_path);
	si->setText(0, p);
	si->setText(1, v);
	si->setIcon(0, icon);
}


void ObjectTree::createObjectsTree(QTreeWidgetItem * root, const SHS::Object & o) {
	QMapIterator<QString, QVariant> pit(o.properties);
	QStringList obj_path = QStringList(o.object_path) << o.name;
	while (pit.hasNext()) {
		pit.next();
		if (pit.key().startsWith("_")) continue;
		addBlockPropertyItem(pit.key(), pit.value().toString(), obj_path, root, icon_property);
	}

	QMapIterator<QString, SHS::Command> it(o.commands);
	while (it.hasNext()) {
		it.next();
		if (it.key().startsWith("_")) continue;
		addBlockPacketItem(it.value(), it.key(), root, SHS::Output, icon_command);
	}

	it = QMapIterator<QString, SHS::Command>(o.commands);
	while (it.hasNext()) {
		it.next();
		if (it.key().startsWith("_")) continue;
		addBlockPacketItem(it.value(), it.key(), root, SHS::Input, icon_signal);
	}

	QMapIterator<QString, SHS::Object> oit(o.objects);
	while (oit.hasNext()) {
		oit.next();
		const SHS::Object & co(oit.value());
		//qDebug() << "createObjectsTree" << oit.key() << co.properties << co.commands.size() << co.objects.size();

		QTreeWidgetItem * bi = new QTreeWidgetItem();
		bi->setData(0, DataType, itObject);
		bi->setIcon(0, icon_object);
		bi->setText(0, oit.key());
		//bi->setText(1, o.properties["name"].toString());

		createObjectsTree(bi, co);
		root->addChild(bi);
	}
}


void ObjectTree::on_tree_itemDoubleClicked(QTreeWidgetItem * item, int ) {
	if (!item) return;
	ItemType type = (ItemType)item->data(0, DataType).toInt();
	if (type == itProperty) {
		emit propertyDoubleClicked(item->data(0, DataPath).toStringList() << item->text(0));
	}
	if (type == itPacket) {
		emit commandDoubleClicked(item->data(0, DataCmd).value<SHS::Command>());
	}
}


void ObjectTree::on_tree_currentItemChanged(QTreeWidgetItem * item, QTreeWidgetItem *) {
	if (!item) return;
	ItemType type = (ItemType)item->data(0, DataType).toInt();
	if (type == itProperty) {
		emit currentPropertyChanged(item->data(0, DataPath).toStringList() << item->text(0));
	}
	if (type == itPacket) {
		emit currentCommandChanged(item->data(0, DataCmd).value<SHS::Command>());
	}

}


void ObjectTree::sessionSave(QByteArray * data) {
	ChunkStream cs;
	cs.add(1, ui->buttonShowBlocksProperties->isChecked())
	  .add(2, ui->buttonShowBlocksEvents->isChecked())
	  .add(3, ui->buttonShowBlocksCommands->isChecked());
	*data = cs.data();
}


void ObjectTree::sessionLoad(QByteArray * data) {
	ChunkStream cs(data);
	while (!cs.atEnd()) {
		switch (cs.read()) {
		case 1: ui->buttonShowBlocksProperties->setChecked(cs.getData<bool>()); break;
		case 2: ui->buttonShowBlocksEvents->setChecked(cs.getData<bool>()); break;
		case 3: ui->buttonShowBlocksCommands->setChecked(cs.getData<bool>()); break;
		}
	}
}


void ObjectTree::filterBlocks() {
	QTreeWidgetItem * root = ui->tree->invisibleRootItem();
	filterBlockTree(root, false);
	root->setHidden(false);
}


bool ObjectTree::filterBlockTree(QTreeWidgetItem * root, bool obj_vis) {
	QString f = ui->lineFilter->text();
	bool pbc = ui->buttonShowBlocksProperties->isChecked();
	bool ebc = ui->buttonShowBlocksEvents->isChecked();
	bool cbc = ui->buttonShowBlocksCommands->isChecked();
	bool ret = false;
	for (int i = 0; i < root->childCount(); ++i) {
		QTreeWidgetItem * ti = root->child(i);
		QString t0 = ti->text(0), t1 = ti->text(1);
		ItemType tp = (ItemType)ti->data(0, DataType).toInt();
		bool vis = false, viso = obj_vis;
		if (tp == itObject) {
			if (!t0.isEmpty() && t0.contains(f, Qt::CaseInsensitive)) viso = true;
			vis = filterBlockTree(ti, viso);
		} else {
			if (viso) vis = true;
			else {
				SHS::PacketFlag fl = (SHS::PacketFlag)ti->data(0, DataFlags).toInt();
				if (((tp == itProperty) && pbc) ||
					((tp == itPacket && fl == SHS::Input ) && ebc) ||
					((tp == itPacket && fl == SHS::Output) && cbc)) {
					if (f.isEmpty()) vis = true;
					else {
						if (!t0.isEmpty() && t0.contains(f, Qt::CaseInsensitive)) vis = true;
						if (!t1.isEmpty() && t1.contains(f, Qt::CaseInsensitive)) vis = true;
					}
				}
			}
		}
		if (vis) ret = true;
		ti->setHidden(!vis);
	}
	return ret;
}
