//! \~\file pivariantsimple.h
//! \~\ingroup Types
//! \~\brief
//! \~english Simple variant type
//! \~russian Простой вариативный тип
/*
    PIP - Platform Independent Primitives
    Variant simple type
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIVARIANTSIMPLE_H
#define PIVARIANTSIMPLE_H

#include "pistring.h"

#include <typeinfo>
#ifdef MICRO_PIP
#  include "pivariant.h"
#endif


class __VariantFunctionsBase__ {
public:
	virtual __VariantFunctionsBase__ * instance() { return 0; }
	virtual PIString typeName() const { return PIString(); }
	virtual uint hash() const { return 0; }
	virtual void newT(void *& ptr, const void * value) { ; }
	virtual void newNullT(void *& ptr) { ; }
	virtual void assignT(void *& ptr, const void * value) { ; }
	virtual void deleteT(void *& ptr) { ; }
};


template<typename T>
class __VariantFunctions__: public __VariantFunctionsBase__ {
public:
	__VariantFunctionsBase__ * instance() final {
		static __VariantFunctions__<T> ret;
		return &ret;
	}
#ifdef MICRO_PIP
	PIString typeName() const final {
		static PIString ret(PIVariant(T()).typeName());
		return ret;
	}
#else
	PIString typeName() const final {
		static PIString ret(typeid(T).name());
		return ret;
	}
#endif
	uint hash() const final {
		static uint ret = typeName().hash();
		return ret;
	}
	void newT(void *& ptr, const void * value) final { ptr = (void *)(new T(*(const T *)value)); }
	void newNullT(void *& ptr) final { ptr = (void *)(new T()); }
	void assignT(void *& ptr, const void * value) final { *(T *)ptr = *(const T *)value; }
	void deleteT(void *& ptr) final { delete (T *)(ptr); }
};


//! \~\ingroup Types
//! \~\brief
//! \~english Lightweight strongly-typed variant for a single value.
//! \~russian Легковесный строго типизированный вариант для одного значения.
class PIVariantSimple {
public:
	//! \~english Constructs an empty %PIVariantSimple.
	//! \~russian Создает пустой %PIVariantSimple.
	PIVariantSimple() {
		ptr = 0;
		f   = 0;
	}

	//! \~english Constructs a copy of %PIVariantSimple.
	//! \~russian Создает копию %PIVariantSimple.
	PIVariantSimple(const PIVariantSimple & v) {
		ptr = 0;
		f   = v.f;
		if (f && v.ptr) f->newT(ptr, v.ptr);
	}

	//! \~english Destroys the stored value, if any.
	//! \~russian Удаляет сохраненное значение, если оно есть.
	~PIVariantSimple() { destroy(); }

	//! \~english Replaces this object with a copy of another %PIVariantSimple.
	//! \~russian Заменяет содержимое копией другого %PIVariantSimple.
	PIVariantSimple & operator=(const PIVariantSimple & v) {
		destroy();
		f = v.f;
		if (f && v.ptr) f->newT(ptr, v.ptr);
		return *this;
	}

	//! \~english Stores value `v`.
	//! \~russian Сохраняет значение `v`.
	//! \~\details
	//! \~english Reuses the existing storage when the stored type already matches `T`.
	//! \~russian Переиспользует существующее хранилище, если сохраненный тип уже совпадает с `T`.
	template<typename T>
	void setValue(const T & v) {
		if (f) {
			if (isMyType<T>()) {
				f->assignT(ptr, (const void *)&v);
				return;
			}
		}
		destroy();
		f = __VariantFunctions__<T>().instance();
		f->newT(ptr, (const void *)&v);
	}

	//! \~\brief
	//! \~english Returns the stored value as type `T`.
	//! \~russian Возвращает сохраненное значение как тип `T`.
	//! \~\details
	//! \~english
	//! Unlike %PIVariant, this class checks the exact stored type.
	//! Returns the value only when the currently stored type matches `T`.
	//!
	//! \~russian
	//! В отличие от %PIVariant этот класс строго проверяет точный тип.
	//! Возвращает значение только если сейчас сохранен именно тип `T`.
	//!
	template<typename T>
	T value() const {
		if (!f) return T();
		if (!isMyType<T>()) return T();
		return *(T *)(ptr);
	}

	//! \~english Creates %PIVariantSimple initialized with value `v`.
	//! \~russian Создает %PIVariantSimple, инициализированный значением `v`.
	template<typename T>
	static PIVariantSimple fromValue(const T & v) {
		PIVariantSimple ret;
		ret.setValue(v);
		return ret;
	}

private:
	template<typename T>
	bool isMyType() const {
		if (!f) return false;
		uint mh = f->hash(), th = __VariantFunctions__<T>().instance()->hash();
		if (mh == 0 || th == 0) return false;
		return mh == th;
	}

	void destroy() {
		if (ptr && f) f->deleteT(ptr);
		ptr = 0;
		f   = 0;
	}

	void * ptr;
	__VariantFunctionsBase__ * f;
};


//! \relatesalso PIVariantSimple
//! \~\brief
//! \~english Registers a readable type name for %PIVariantSimple.
//! \~russian Регистрирует читаемое имя типа для %PIVariantSimple.
#define REGISTER_PIVARIANTSIMPLE(Type)                                                                       \
	template<>                                                                                               \
	class __VariantFunctions__<Type>: public __VariantFunctionsBase__ {                                      \
	public:                                                                                                  \
		__VariantFunctionsBase__ * instance() final {                                                        \
			static __VariantFunctions__<Type> ret;                                                           \
			return &ret;                                                                                     \
		}                                                                                                    \
		PIString typeName() const final {                                                                    \
			static PIString ret(#Type);                                                                      \
			return ret;                                                                                      \
		}                                                                                                    \
		uint hash() const final {                                                                            \
			static uint ret = typeName().hash();                                                             \
			return ret;                                                                                      \
		}                                                                                                    \
		void newT(void *& ptr, const void * value) final { ptr = (void *)(new Type(*(const Type *)value)); } \
		void newNullT(void *& ptr) final { ptr = (void *)(new Type()); }                                     \
		void assignT(void *& ptr, const void * value) final { *(Type *)ptr = *(const Type *)value; }         \
		void deleteT(void *& ptr) final { delete (Type *)(ptr); }                                            \
	};

REGISTER_PIVARIANTSIMPLE(std::function<void(void *)>)

#endif // PIVARIANTSIMPLE_H
