#ifndef QTJSONSERIALIZER_METAWRITERS_H
#define QTJSONSERIALIZER_METAWRITERS_H

#include "qtjsonserializer_global.h"

#include <QtCore/qmetatype.h>
#include <QtCore/qvariant.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qhash.h>
#include <QtCore/qreadwritelock.h>

#include <QtCore/qset.h>

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QtCore/qlinkedlist.h>
#endif

namespace QtJsonSerializer::MetaWriters {

class SequentialWriterFactory;
//! The writer class for sequential containers
class Q_JSONSERIALIZER_EXPORT SequentialWriter
{
	Q_DISABLE_COPY(SequentialWriter)

public:
	//! Information about a sequential container
	struct SequenceInfo {
		//! The containers data type
		int type = QMetaType::UnknownType;
		//! Specifies if the container is a finite set
		bool isSet = false;
	};

	//! Registers a container factory for the given container and value classes
	template <template<typename> class TContainer, typename TClass>
	static void registerWriter();
	//! @copybrief SequentialWriter::registerWriter()
	static void registerWriter(int metaTypeId, SequentialWriterFactory *factory);
	//! Checks if a writer exists for the given type
	static bool canWrite(int metaTypeId);
	//! Returns a writer instance for the given data, or nullptr if none found
	static QSharedPointer<SequentialWriter> getWriter(QVariant &data);
	//! Returns the information details of the given type
	static SequenceInfo getInfo(int metaTypeId);

	virtual ~SequentialWriter();
	//! Return the information for the wrapped container
	virtual SequenceInfo info() const = 0;
	//! Reserves space for size elements in the container
	virtual void reserve(int size) = 0;
	//! Adds an element to the "end" of the container
	virtual void add(const QVariant &value) = 0;

protected:
	//! @private
	SequentialWriter();
};

//! A factory to create sequential writer instances from variant data
class Q_JSONSERIALIZER_EXPORT SequentialWriterFactory
{
	Q_DISABLE_COPY(SequentialWriterFactory)

public:
	SequentialWriterFactory();
	virtual ~SequentialWriterFactory();
	//! Factory method to create the instance. data can be null for read-only writers
	virtual QSharedPointer<SequentialWriter> create(void *data) const = 0;
};



class AssociativeWriterFactory;
//! The writer class for associative containers
class Q_JSONSERIALIZER_EXPORT AssociativeWriter
{
	Q_DISABLE_COPY(AssociativeWriter)

public:
	//! Information about a associative container
	struct AssociationInfo {
		//! The type of the associations keys
		int keyType = QMetaType::UnknownType;
		//! The type of the associations values
		int valueType = QMetaType::UnknownType;
	};

	//! Registers a container factory for the given container, key and value classes
	template <template<typename, typename> class TContainer, typename TKey, typename TValue>
	static void registerWriter();
	//! @copybrief AssociativeWriter::registerWriter()
	static void registerWriter(int metaTypeId, AssociativeWriterFactory *factory);
	//! Checks if a writer exists for the given type
	static bool canWrite(int metaTypeId);
	//! Returns a writer instance for the given data, or nullptr if none found
	static QSharedPointer<AssociativeWriter> getWriter(QVariant &data);
	//! Returns the information details of the given type
	static AssociationInfo getInfo(int metaTypeId);

	virtual ~AssociativeWriter();
	//! Return the information for the wrapped container
	virtual AssociationInfo info() const = 0;
	//! Inserts the given value for the given key into the container
	virtual void add(const QVariant &key, const QVariant &value) = 0;

protected:
	//! @private
	AssociativeWriter();
};

//! A factory to create associative writer instances from variant data
class Q_JSONSERIALIZER_EXPORT AssociativeWriterFactory
{
	Q_DISABLE_COPY(AssociativeWriterFactory)

public:
	AssociativeWriterFactory();
	virtual ~AssociativeWriterFactory();
	//! Factory method to create the instance. data can be null for read-only writers
	virtual QSharedPointer<AssociativeWriter> create(void *data) const = 0;
};

// ------------- Generic Implementation classes -------------

namespace Implementations {

template <template<typename> class TContainer, typename TClass>
class SequentialWriterImpl final : public SequentialWriter
{
public:
	SequentialWriterImpl(TContainer<TClass> *data)
		: _data{data}
	{}

	SequenceInfo info() const final {
		return {qMetaTypeId<TClass>(), false};
	}

	void reserve(int size) final {
		_data->reserve(size);
	}

	void add(const QVariant &value) final {
		_data->append(value.template value<TClass>());
	}

private:
	TContainer<TClass> *_data;
};

template <template<typename> class TContainer, typename TClass>
class SequentialWriterFactoryImpl final : public SequentialWriterFactory
{
public:
	QSharedPointer<SequentialWriter> create(void *data) const final {
		return QSharedPointer<SequentialWriterImpl<TContainer, TClass>>::create(reinterpret_cast<TContainer<TClass>*>(data));
	}
};



template <template<typename, typename> class TContainer, typename TKey, typename TValue>
class AssociativeWriterImpl final : public AssociativeWriter
{
public:
	AssociativeWriterImpl(TContainer<TKey, TValue> *data)
		: _data{data}
	{}

	AssociationInfo info() const final {
		return {qMetaTypeId<TKey>(), qMetaTypeId<TValue>()};
	}

	void add(const QVariant &key, const QVariant &value) final {
		_data->insert(key.template value<TKey>(),
					  value.template value<TValue>());
	}

private:
	TContainer<TKey, TValue> *_data;
};

template <template<typename, typename> class TContainer, typename TKey, typename TValue>
class AssociativeWriterFactoryImpl final : public AssociativeWriterFactory
{
public:
	QSharedPointer<AssociativeWriter> create(void *data) const final {
		return QSharedPointer<AssociativeWriterImpl<TContainer, TKey, TValue>>::create(reinterpret_cast<TContainer<TKey, TValue>*>(data));
	}
};

// ------------- Specializations and base generic implementations -------------

template <typename TClass>
class SequentialWriterImpl<QSet, TClass> final : public SequentialWriter
{
public:
	SequentialWriterImpl(QSet<TClass> *data)
		: _data{data}
	{}

	SequenceInfo info() const final {
		return {qMetaTypeId<TClass>(), true};
	}

	void reserve(int size) final {
		_data->reserve(size);
	}

	void add(const QVariant &value) final {
		_data->insert(value.template value<TClass>());
	}

private:
	QSet<TClass> *_data;
};

#if !defined(QT_NO_LINKED_LIST) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
template <typename TClass>
class SequentialWriterImpl<QLinkedList, TClass> final : public SequentialWriter
{
public:
	SequentialWriterImpl(QLinkedList<TClass> *data)
		: _data{data}
	{}

	SequenceInfo info() const final {
		return {qMetaTypeId<TClass>(), false};
	}

	void reserve(int) final {}

	void add(const QVariant &value) final {
		_data->append(value.template value<TClass>());
	}

private:
	QLinkedList<TClass> *_data;
};
#endif

template <>
class SequentialWriterImpl<QList, QVariant> final : public SequentialWriter
{
public:
	SequentialWriterImpl(QVariantList *data);

	SequenceInfo info() const final;
	void reserve(int size) final;
	void add(const QVariant &value) final;

private:
	QVariantList *_data;
};



template <>
class AssociativeWriterImpl<QMap, QString, QVariant> final : public AssociativeWriter
{
public:
	AssociativeWriterImpl(QVariantMap *data);

	AssociationInfo info() const final;
	void add(const QVariant &key, const QVariant &value) final;

private:
	QVariantMap *_data;
};

template <>
class AssociativeWriterImpl<QHash, QString, QVariant> final : public AssociativeWriter
{
public:
	AssociativeWriterImpl(QVariantHash *data);

	AssociationInfo info() const final;
	void add(const QVariant &key, const QVariant &value) final;

private:
	QVariantHash *_data;
};

}

template<template<typename> class TContainer, typename TClass>
void SequentialWriter::registerWriter()
{
	registerWriter(qMetaTypeId<TContainer<TClass>>(),
				   new Implementations::SequentialWriterFactoryImpl<TContainer, TClass>{});
}

template<template<typename, typename> class TContainer, typename TKey, typename TValue>
void AssociativeWriter::registerWriter()
{
	registerWriter(qMetaTypeId<TContainer<TKey, TValue>>(),
				   new Implementations::AssociativeWriterFactoryImpl<TContainer, TKey, TValue>{});
}

}

#endif // QTJSONSERIALIZER_METAWRITERS_H
