#include "pivaluetree_edit_parameters.h"

#include "piqt.h"
#include "pivaluetree_edit.h"
#include "pivariant_edit.h"

#include <QFormLayout>
#include <QGroupBox>
#include <QMetaEnum>
#include <QActionGroup>

using Attribute            = PIValueTree::Attribute;

const char property_name[] = "__name__";


PIValueTreeEditParameters::PIValueTreeEditParameters(QWidget * parent): QDialog(parent) {
	setupUi(this);
	auto * ag = new QActionGroup(this);
	ag->setExclusive(true);
	// connect(ag, &QActionGroup::triggered, [this](QAction * a) { widget->setGrouping((PIValueTreeEdit::Grouping)a->data().toInt()); });
	auto mo = PIValueTreeEdit::staticMetaObject;
	auto me = mo.enumerator(mo.indexOfEnumerator("Grouping"));
	for (int i = 0; i < me.keyCount(); ++i) {
		auto * a = ag->addAction(me.key(i));
		a->setCheckable(true);
		a->setData(me.value(i));
		if (me.value(i) == PIValueTreeEdit::Groups) a->setChecked(true);
	}
	menu_grouping.addActions(ag->actions());
	menu_grouping.menuAction()->setText(tr("Grouping"));
}


PIValueTreeEditParameters::~PIValueTreeEditParameters() {}


bool PIValueTreeEditParameters::showFor(PIValueTree & vt) {
	setWindowTitle(tr("Change of \"%1\"").arg(PI2QString(vt.name())));
	ve_attr.clear();
	ve_array.clear();
	while (layoutAttributes->rowCount() > 0)
		layoutAttributes->removeRow(0);
	while (layoutArray->rowCount() > 0)
		layoutArray->removeRow(0);

	if (last_IDs_count != PIVariant::knownTypeIDsCount()) {
		last_IDs_count = PIVariant::knownTypeIDsCount();
		comboType->clear();
		auto ids = PIVariant::knownTypeIDs();
		PIVector<PIPair<uint, QString>> types;
		for (auto id: ids) {
			if (!PIVariantEditorBase::editorExists(id)) continue;
			PIString tn = PIVariant::typeNameFromID(id);
			if (tn.startsWith("PI")) tn.remove(0, 2);
			if (tn.startsWith("VariantTypes::")) tn.remove(0, 14);
			types.append({id, PI2QString(tn)});
		}
		types.sort([](const PIPair<uint, QString> & a, const PIPair<uint, QString> & b) {
			return QString::localeAwareCompare(a.second, b.second) < 0;
		});
		for (const auto & t: types)
			comboType->addItem(t.second, t.first);
	}

	comboType->blockSignals(true);
	comboType->setEnabled(true);
	if (vt.isArray()) {
		uint array_type = PIVariant::typeIDFromName(vt.attribute(Attribute::arrayType).toString());
		comboType->setCurrentIndex(comboType->findData(array_type));
		checkAttribute(vt, Attribute::arrayMinCount, 0);
		checkAttribute(vt, Attribute::arrayMaxCount, 65536);
		checkAttribute(vt, Attribute::arrayReorder, true);
		checkAttribute(vt, Attribute::arrayResize, true);
		createAttributes(ve_array, layoutArray, vt.attributes(), true);
		groupArray->show();
	} else {
		if (vt.attribute(Attribute::isLabel, false).toBool()) {
			comboType->setEnabled(false);
			checkAttribute(vt, Attribute::style, "");
		}
		comboType->setCurrentIndex(comboType->findData(vt.value().typeID()));
		groupArray->hide();
	}
	comboType->blockSignals(false);
	checkHidden->setChecked(vt.attribute(Attribute::hidden, false).toBool());
	checkReadOnly->setChecked(vt.attribute(Attribute::readOnly, false).toBool());
	checkLabel->blockSignals(true);
	checkLabel->setChecked(vt.attribute(Attribute::isLabel, false).toBool());
	checkLabel->blockSignals(false);
	lineComment->setText(PI2QString(vt.comment()));
	lineToolTip->setText(PI2QString(vt.attribute(Attribute::toolTip).toString()));
	createAttributes(ve_attr, layoutAttributes, vt.attributes());

	if (exec() != QDialog::Accepted) return false;

	if (!vt.isArray()) {
		PIString vs   = vt.value().toString();
		PIVariant var = PIVariant::fromType(comboType->currentData().toUInt());
		var.setValueFromString(vs);
		vt.setValue(var);
	}
	applyAttributes(vt);
	vt.setComment(Q2PIString(lineComment->text()));

	return true;
}


void PIValueTreeEditParameters::createAttributes(QList<PIVariantEdit *> & list,
												 QFormLayout * lay,
												 const PIVariantMap & attr,
												 bool inv_filter) {
	static PIStringList hidden({"type",
								Attribute::hidden,
								Attribute::readOnly,
								Attribute::toolTip,
								Attribute::isLabel,
								Attribute::arrayType,
								Attribute::expression});
	static PIStringList filter({Attribute::arrayMinCount, Attribute::arrayMaxCount, Attribute::arrayReorder, Attribute::arrayResize});
	list.clear();
	while (lay->rowCount() > 0)
		lay->removeRow(0);
	PIVariantMap dal;
	if (!inv_filter) dal = PIVariantEditorBase::editorDefaultAttributes(comboType->currentData().toUInt());
	auto vit = attr.makeIterator();
	while (vit.next()) {
		if (hidden.contains(vit.key())) continue;
		bool cf = filter.contains(vit.key());
		if (cf && !inv_filter) continue;
		if (!cf && inv_filter) continue;
		dal[vit.key()] = vit.value();
	}
	auto addEdit = [&list, &lay](const QString & name, const PIVariant & value) {
		auto * ve = new PIVariantEdit();
		ve->setAttributes(PIVariantEditorBase::editorDefaultAttributes(value.typeID()));
		ve->setValue(value);
		ve->setProperty(property_name, name);
		list << ve;
		lay->addRow(name + ":", ve);
	};
	const auto & sal(PIValueTree::standardAttributes());
	for (const auto & sn: sal) {
		if (!dal.contains(sn)) continue;
		addEdit(PI2QString(sn), dal.value(sn));
		dal.remove(sn);
	}
	auto dit = dal.makeIterator();
	while (dit.next()) {
		addEdit(PI2QString(dit.key()), dit.value());
	}
}


void PIValueTreeEditParameters::applyAttributes(PIValueTree & vt) {
	bool is_array = vt.isArray();
	vt.attributes().clear();
	for (auto * w: ve_attr) {
		PIString an = Q2PIString(w->property(property_name).toString());
		vt.setAttribute(an, w->value());
	}
	if (is_array) {
		vt.setAttribute(Attribute::arrayType, PIVariant::typeNameFromID(comboType->currentData().toUInt()));
		for (auto * w: ve_array) {
			PIString an = Q2PIString(w->property(property_name).toString());
			vt.setAttribute(an, w->value());
		}
	}
	vt.setAttribute(Attribute::hidden, checkHidden->isChecked());
	vt.setAttribute(Attribute::readOnly, checkReadOnly->isChecked());
	vt.setAttribute(Attribute::isLabel, checkLabel->isChecked());
	vt.setAttribute(Attribute::toolTip, Q2PIString(lineToolTip->text()));
}


void PIValueTreeEditParameters::checkAttribute(PIValueTree & vt, PIString an, PIVariant def) {
	if (vt.attributes().contains(an)) def.setValueFromString(vt.attributes().value(an).toString());
	vt.setAttribute(an, def);
}


void PIValueTreeEditParameters::on_comboType_currentIndexChanged(int) {
	createAttributes(ve_attr, layoutAttributes);
}


void PIValueTreeEditParameters::on_checkLabel_toggled(bool on) {
	if (!on) {
		comboType->setEnabled(true);
		for (int r = 0; r < layoutAttributes->rowCount(); ++r) {
			auto * w = qobject_cast<PIVariantEdit *>(layoutAttributes->itemAt(r, QFormLayout::FieldRole)->widget());
			if (!w) continue;
			PIString an = Q2PIString(w->property(property_name).toString());
			if (an == Attribute::style) {
				ve_attr.removeOne(w);
				layoutAttributes->removeRow(r);
				break;
			}
		}
	} else {
		comboType->setEnabled(false);
		comboType->setCurrentIndex(comboType->findData(PIVariant::typeID<PIString>()));
		ve_attr.clear();
		while (layoutAttributes->rowCount() > 0)
			layoutAttributes->removeRow(0);
		// clang-format off
		createAttributes(ve_attr, layoutAttributes, {{Attribute::style, ""}});
		// clang-format on
	}
}
