#include <piqt.h>
#include "SH_ui_sync_logic.h"

const char hashName[] = "_shs_hash_";



void UiSync::setSyncProperties(const PIMap<QString, QVector<QByteArray> > & names) {
	property_names = names;
	properties_by_classname.clear();
	auto it = property_names.makeIterator();
	while (it.next()) {
		auto & map(properties_by_classname[it.key()]);
		for (const auto & p: it.value())
			map[stringHash(QString::fromLatin1(p))] = QString::fromLatin1(p);
	}
}


const QMap<uint, QString> & UiSync::propertiesByClassname(const QString & cn) {
	return properties_by_classname[cn];
}


uint UiSync::getObjectHash(const QObject * o) {
	return o->property(hashName).toUInt();
}


uint UiSync::createObjectHash(const QObject * o) {
	return piHash(Q2PIString(o->objectName())) ^ piHash(PIString(o->metaObject()->className()));
}


void UiSync::writeObjectHash(QObject * o) {
	o->setProperty(hashName, createObjectHash(o));
}


uint UiSync::stringHash(const QString & s) {
	return piHash(Q2PIString(s));
}


QByteArray UiSync::serializeDiff(const ObjectPropertiesMap & diff) {
	return PI2QByteArray(piSerialize(diff));
}


UiSync::ObjectPropertiesMap UiSync::deserializeDiff(const QByteArray & diff) {
	return piDeserialize<ObjectPropertiesMap>(Q2PIByteArray(diff));
}


void UiSync::addObjectProperty(ObjectProperties & ret, const QObject * o, const char * pname) {
	int pi = o->metaObject()->indexOfProperty(pname);
	if (pi >= 0) {
		ret[stringHash(pname)] = o->property(pname);
		return;
	}
	if (o->dynamicPropertyNames().contains(pname)) {
		ret[stringHash(pname)] = o->property(pname);
		return;
	}
	qDebug() << "Warning: property" << pname << "doesn`t exists in class" << o->metaObject()->className() << "!";
}


UiSync::ObjectProperties UiSync::makeObjectProperties(const QObject * o) {
	ObjectProperties ret;
	//addObjectProperty(o, ret, "enabled");
	//addObjectProperty(o, ret, "visible");
	auto pl = property_names.value(o->metaObject()->className());
	for (const auto & s: pl)
		addObjectProperty(ret, o, s.constData());
	return ret;
}


UiSync::ObjectPropertiesMap UiSync::makeObjectListProperties(QObjectList list) {
	ObjectPropertiesMap ret;
	for (const auto * w: list) {
		auto op = makeObjectProperties(w);
		if (op.isNotEmpty())
			ret[getObjectHash(w)] = op;
	}
	return ret;
}


void UiSync::overrideObjectProperties(ObjectProperties & dst, const ObjectProperties & income) {
	auto it = income.makeIterator();
	while (it.next())
		dst[it.key()] = it.value();
}


void UiSync::overrideObjectListProperties(ObjectPropertiesMap & dst, const ObjectPropertiesMap & income) {
	auto it = income.makeIterator();
	while (it.next())
		overrideObjectProperties(dst[it.key()], it.value());
}


UiSync::ObjectProperties UiSync::objectPropertiesDiff(const ObjectProperties & src, const ObjectProperties & income) {
	ObjectProperties ret;
	auto it = income.makeIterator();
	while (it.next()) {
		if (src.contains(it.key()))
			if (src.value(it.key()) == it.value())
				continue;
		ret[it.key()] = it.value();
	}
	return ret;
}


UiSync::ObjectPropertiesMap UiSync::objectListPropertiesDiff(const ObjectPropertiesMap & src, const ObjectPropertiesMap & income) {
	ObjectPropertiesMap ret;
	auto it = income.makeIterator();
	while (it.next()) {
		auto opd = objectPropertiesDiff(src.value(it.key()), it.value());
		if (opd.isNotEmpty())
			ret[it.key()] = opd;
	}
	return ret;
}
