#include "versionnumberconverter_p.h"
#include "exception.h"
#include "cborserializer.h"

#include <QtCore/QVersionNumber>
using namespace QtJsonSerializer;
using namespace QtJsonSerializer::TypeConverters;

Q_LOGGING_CATEGORY(QtJsonSerializer::TypeConverters::logVersionConverter, "qt.jsonserializer.converter.versionnumber")

bool VersionNumberConverter::canConvert(int metaTypeId) const
{
	return metaTypeId == qMetaTypeId<QVersionNumber>();
}

QList<QCborTag> VersionNumberConverter::allowedCborTags(int metaTypeId) const
{
	Q_UNUSED(metaTypeId)
	return {static_cast<QCborTag>(CborSerializer::VersionNumber)};
}

QList<QCborValue::Type> VersionNumberConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
{
	Q_UNUSED(metaTypeId)
	Q_UNUSED(tag)
	return {QCborValue::Array, QCborValue::String};
}

int VersionNumberConverter::guessType(QCborTag tag, QCborValue::Type dataType) const
{
	if (tag == static_cast<QCborTag>(CborSerializer::VersionNumber) &&
		allowedCborTypes(QMetaType::UnknownType, tag).contains(dataType))
		return qMetaTypeId<QVersionNumber>();
	else
		return QMetaType::UnknownType;
}

QCborValue VersionNumberConverter::serialize(int propertyType, const QVariant &value) const
{
	Q_UNUSED(propertyType)
	const auto version = value.value<QVersionNumber>();
	if (helper()->getProperty("versionAsString").toBool())
		return {static_cast<QCborTag>(CborSerializer::VersionNumber), version.toString()};
	else {
		QCborArray array;
		for (const auto segment : version.segments())
			array.append(segment);
		return {static_cast<QCborTag>(CborSerializer::VersionNumber), array};
	}
}

QVariant VersionNumberConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
{
	Q_UNUSED(propertyType)
	Q_UNUSED(parent)
	const auto cValue = (value.isTag() ? value.taggedValue() : value);
	if (cValue.type() == QCborValue::Array) {
		const auto cArray = cValue.toArray();
		QVector<int> segments;
		segments.reserve(static_cast<int>(cArray.size()));
		auto i = 0;
		for (const auto cElem : cArray) {
			if (!cElem.isInteger()) {
				throw DeserializationException{"Segment at position " + QByteArray::number(i) +
													" is not an integer - a version number must be integers only!"};
			}
			segments.append(static_cast<int>(cElem.toInteger()));
			++i;
		}
		return QVariant::fromValue(QVersionNumber{std::move(segments)});
	} else if (cValue.type() == QCborValue::String) {
		const auto strValue = cValue.toString();
		if (!strValue.isEmpty()) {
			int suffixIndex = -1;
			const auto version = QVersionNumber::fromString(strValue, &suffixIndex);
			if (version.isNull())
				throw DeserializationException("Invalid version number, no segments found");
			if (suffixIndex < strValue.size())
				qCWarning(logVersionConverter) << "Parsed QVersionNumber with suffix - suffixes are discarded!";
			return QVariant::fromValue(version);
		} else
			return QVariant::fromValue(QVersionNumber{});
	} else
		throw DeserializationException{"Invalid type id"};
}
