#include "confignode.hpp"

#include <qjsonvalue.h>
#include <qloggingcategory.h>
#include <qmetaobject.h>
#include <qstring.h>

namespace ZShell::config {

Q_LOGGING_CATEGORY(lcConfig, "zshell.config")

ConfigNode::ConfigNode(QObject* parent) : QObject(parent) {
	m_batchTimer = new QTimer(this);
	m_batchTimer->setSingleShot(true);
	m_batchTimer->setInterval(0);
	connect(
		m_batchTimer, &QTimer::timeout, this, &ConfigNode::emitBatchedChanges);
}

void ConfigNode::loadFromJsonQuietly(const QJsonValue& json) {
	setNotifySuppressed(true);
	loadFromJson(json);
	setNotifySuppressed(false);
}

void ConfigNode::setNotifySuppressed(bool suppressed) {
	m_suppressNotify = suppressed;

	const auto children = childNodes();
	for (auto* const child : children)
		child->setNotifySuppressed(suppressed);
}

QList<ConfigNode*> ConfigNode::childNodes() const {
	return {};
}

void ConfigNode::syncFromGlobal(ConfigNode* global) {
	m_global = global;
	resyncFromGlobal();
}

bool ConfigNode::isOverlay() const {
	return m_global != nullptr && this != m_global;
}

QString ConfigNode::propertyPath(const QString& name) const {
	if (name.isEmpty()) return objectName();

	QObject* const parentObj = parent();
	if (parentObj) {
		if (auto* parentNode = qobject_cast<ConfigNode*>(parentObj)) {
			const auto childName = parentNode->childPath(this);
			if (!childName.isEmpty()) {
				return joinPath(parentNode->propertyPath(childName), name);
			}
		}
	}
	return name;
}

int ConfigNode::basePropertyOffset() {
	return QObject::staticMetaObject.propertyCount();
}

void ConfigNode::notifyPropertyChanged(
	const QString& name, const QVariant& value) {
	if (m_suppressNotify) return;

	m_pendingChanges[name] = value;
	if (m_batchTimer && !m_batchTimer->isActive()) m_batchTimer->start();
}

QString ConfigNode::joinPath(const QString& parent, const QString& child) {
	// List elements are addressed as parent[i], everything else as parent.child
	if (child.startsWith(QLatin1Char('['))) return parent + child;

	return parent + QLatin1Char('.') + child;
}

void ConfigNode::emitBatchedChanges() {
	if (m_pendingChanges.isEmpty()) return;

	QMap<QString, QVariant> changes = m_pendingChanges;
	m_pendingChanges.clear();
	emit propertiesChanged(changes);
}

} // namespace ZShell::config
