/*! \file pivariant.h
 * \brief Variant type
 * 
 * This file declares PIVariant
*/
/*
    PIP - Platform Independent Primitives
    Variant type
    Copyright (C) 2015  Ivan Pelipenko peri4ko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU 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 General Public License for more details.

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

#ifndef PIVARIANT_H
#define PIVARIANT_H

#include "pistring.h"
#include "pibitarray.h"
#include "pitime.h"
#include "pimathbase.h"

#define __PIVARIANT_UNION_SIZE__ 32

#define _vcomplexd (*((complexd*)_vraw))
#define _vcomplexld (*((complexld*)_vraw))
#define _vtime (*((PITime*)_vraw))
#define _vdate (*((PIDate*)_vraw))
#define _vdatetime (*((PIDateTime*)_vraw))
#define _vsystime (*((PISystemTime*)_vraw))

#define _vvcomplexd(v) (*((complexd*)v._vraw))
#define _vvcomplexld(v) (*((complexld*)v._vraw))
#define _vvtime(v) (*((PITime*)v._vraw))
#define _vvdate(v) (*((PIDate*)v._vraw))
#define _vvdatetime(v) (*((PIDateTime*)v._vraw))
#define _vvsystime(v) (*((PISystemTime*)v._vraw))

class PIP_EXPORT PIVariant {
	friend PICout operator <<(PICout s, const PIVariant & v);
public:
	
	//! Type of %PIVariant content
	enum Type {
		Invalid /** Invalid type , default type of empty contructor */ = 0 ,
		Bool /** bool */ ,
		Char /** char */ ,
		UChar /** uchar */ ,
		Short /** short */ ,
		UShort /** ushort */ ,
		Int /** int */ ,
		UInt /** uint */ ,
		Long /** long */ ,
		ULong /** ulong */ ,
		LLong /** llong */ ,
		ULLong /** ullong */ ,
		Float /** float */ ,
		Double /** double */ ,
		LDouble /** ldouble */ ,
		Complexd /** complexd */ ,
		Complexld /** complexld */ ,
		BitArray /** PIBitArray */ ,
		ByteArray /** PIByteArray */ ,
		String /** PIString */ ,
		StringList /** PIStringList */ ,
		Time /** PITime */ ,
		Date /** PIDate */ ,
		DateTime /** PIDateTime */ ,
		SystemTime /** PISystemTime */ ,
		Custom /** Custom */ = 0xFF
	};
	
	//! Empty constructor, \a type() will be set to \a Invalid
	PIVariant();
	
	//! Constructs variant from string
	PIVariant(const char * v) {setValue(PIString(v));}
	
	//! Constructs variant from boolean
	PIVariant(const bool v) {setValue(v);}
	
	//! Constructs variant from char
	PIVariant(const char v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const uchar v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const short v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const ushort v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const int & v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const uint & v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const long & v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const ulong & v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const llong & v) {setValue(v);}
	
	//! Constructs variant from integer
	PIVariant(const ullong & v) {setValue(v);}
	
	//! Constructs variant from float
	PIVariant(const float & v) {setValue(v);}
	
	//! Constructs variant from double
	PIVariant(const double & v) {setValue(v);}
	
	//! Constructs variant from long double
	PIVariant(const ldouble & v) {setValue(v);}
	
	//! Constructs variant from complex
	PIVariant(const complexd & v) {setValue(v);}
	
	//! Constructs variant from complex
	PIVariant(const complexld & v) {setValue(v);}
	
	//! Constructs variant from bit array
	PIVariant(const PIBitArray & v) {setValue(v);}
	
	//! Constructs variant from byte array
	PIVariant(const PIByteArray & v) {setValue(v);}
	
	//! Constructs variant from string
	PIVariant(const PIString & v) {setValue(v);}
	
	//! Constructs variant from strings list
	PIVariant(const PIStringList & v) {setValue(v);}
	
	//! Constructs variant from time
	PIVariant(const PITime & v) {setValue(v);}
	
	//! Constructs variant from date
	PIVariant(const PIDate & v) {setValue(v);}
	
	//! Constructs variant from date and time
	PIVariant(const PIDateTime & v) {setValue(v);}
	
	//! Constructs variant from system time
	PIVariant(const PISystemTime & v) {setValue(v);}
	
	
	//! Set variant content and type to string
	void setValue(const char * v) {setValue(PIString(v));}
	
	//! Set variant content and type to boolean
	void setValue(const bool v) {type_ = PIVariant::Bool; _vint = (v ? 1 : 0);}
	
	//! Set variant content and type to char
	void setValue(const char v) {type_ = PIVariant::Char; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const uchar v) {type_ = PIVariant::UChar; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const short v) {type_ = PIVariant::Short; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const ushort v) {type_ = PIVariant::UShort; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const int & v) {type_ = PIVariant::Int; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const uint & v) {type_ = PIVariant::UInt; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const long & v) {type_ = PIVariant::Long; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const ulong & v) {type_ = PIVariant::ULong; _vint = v;}
	
	//! Set variant content and type to integer
	void setValue(const llong & v) {type_ = PIVariant::LLong; _vllong = v;}
	
	//! Set variant content and type to integer
	void setValue(const ullong & v) {type_ = PIVariant::ULLong; _vllong = v;}
	
	//! Set variant content and type to float
	void setValue(const float & v) {type_ = PIVariant::Float; _vfloat = v;}
	
	//! Set variant content and type to double
	void setValue(const double & v) {type_ = PIVariant::Double; _vdouble = v;}
	
	//! Set variant content and type to long double
	void setValue(const ldouble & v) {type_ = PIVariant::LDouble; _vldouble = v;}
	
	//! Set variant content and type to complex
	void setValue(const complexd & v) {type_ = PIVariant::Complexd; _vcomplexd = v;}
	
	//! Set variant content and type to complex
	void setValue(const complexld & v) {type_ = PIVariant::Complexld; _vcomplexld = v;}
	
	//! Set variant content and type to bit array
	void setValue(const PIBitArray & v) {type_ = PIVariant::BitArray; _vbitarray = v;}
	
	//! Set variant content and type to byte array
	void setValue(const PIByteArray & v) {type_ = PIVariant::ByteArray; _vbytearray = v;}
	
	//! Set variant content and type to string
	void setValue(const PIString & v) {type_ = PIVariant::String; _vstring = v;}
	
	//! Set variant content and type to strings list
	void setValue(const PIStringList & v) {type_ = PIVariant::StringList; _vstringlist = v;}
	
	//! Set variant content and type to time
	void setValue(const PITime & v) {type_ = PIVariant::Time; _vtime = v;}
	
	//! Set variant content and type to date
	void setValue(const PIDate & v) {type_ = PIVariant::Date; _vdate = v;}
	
	//! Set variant content and type to date and time
	void setValue(const PIDateTime & v) {type_ = PIVariant::DateTime; _vdatetime = v;}
	
	//! Set variant content and type to system time
	void setValue(const PISystemTime & v) {type_ = PIVariant::SystemTime; _vsystime = v;}
	
	
	bool toBool() const;
	int toInt() const;
	llong toLLong() const;
	float toFloat() const;
	double toDouble() const;
	ldouble toLDouble() const;
	complexd toComplexd() const;
	complexld toComplexld() const;
	PITime toTime() const;
	PIDate toDate() const;
	PIDateTime toDateTime() const;
	PISystemTime toSystemTime() const;
	PIString toString() const;
	PIStringList toStringList() const;
	PIBitArray toBitArray() const;
	PIByteArray toByteArray() const;
	
	
	/** \brief Returns variant content as custom type
	* \details In case of known types this function equivalent \a to<Type> function. \n
	* Otherwise returns content as type T. */
	template<typename T>
	T toValue() const {if (_vcustom.size() != sizeof(T)) return T(); return *((T*)_vcustom.data());}
	/*
	operator bool() const {return toBool();}
	operator char() const {return toInt();}
	operator uchar() const {return toInt();}
	operator short() const {return toInt();}
	operator ushort() const {return toInt();}
	operator int() const {return toInt();}
	operator uint() const {return toInt();}
	operator long() const {return toInt();}
	operator ulong() const {return toInt();}
	operator llong() const {return toLLong();}
	operator ullong() const {return (ullong)toLLong();}
	operator float() const {return toFloat();}
	operator double() const {return toDouble();}
	operator ldouble() const {return toLDouble();}
	operator complexd() const {return toComplexd();}
	operator complexld() const {return toComplexld();}
	operator PITime() const {return toTime();}
	operator PIDate() const {return toDate();}
	operator PIDateTime() const {return toDateTime();}
	operator PIString() const {return toString();}
	operator PIStringList() const {return toStringList();}
	operator PIBitArray() const {return toBitArray();}
	operator PIByteArray() const {return toByteArray();}
	operator const char*() const {return toString().data();}
	operator void*() const {return (void*)(toLLong());}
	*/
	
	//! Assign operator
	PIVariant & operator =(const PIVariant & v);
	//! Assign operator
	PIVariant & operator =(const char * v) {setValue(PIString(v)); return *this;}
	//! Assign operator
	PIVariant & operator =(const bool v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const char v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const uchar v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const short v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const ushort v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const int & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const uint & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const long & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const ulong & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const llong & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const ullong & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const float & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const double & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const ldouble & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const complexd & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const complexld & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIBitArray & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIByteArray & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIString & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIStringList & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PITime & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIDate & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PIDateTime & v) {setValue(v); return *this;}
	//! Assign operator
	PIVariant & operator =(const PISystemTime & v) {setValue(v); return *this;}
	
	
	//! Compare operator
	bool operator ==(const PIVariant & v) const;
	//! Compare operator
	bool operator !=(const PIVariant & v) const {return !(*this == v);}
	
	
	//! Returns type of variant content
	PIVariant::Type type() const {return type_;}
	
	//! Returns type name of variant content
	PIString typeName() const {return typeName(type_);}
	
	
	//! Returns \b true if type is not Invalid
	bool isValid() const {return type_ != PIVariant::Invalid;}
	
	
	/** \brief Returns new variant from custom type
	* \details In case of known types this function equivalent \a PIVariant(T) constructors. \n
	* Otherwise returns variant with content \a v and type Custom. */
	template <typename T>
	static PIVariant fromValue(const T & v) {PIVariant ret; ret._vcustom.resize(sizeof(T)); new((T*)(ret._vcustom.data()))T(v); ret.type_ = PIVariant::Custom; return ret;}
	
	
	//! Returns type from name
	static PIVariant::Type typeFromName(const PIString & tname);
	
	//! Returns type name
	static PIString typeName(PIVariant::Type type);
	
private:
	void destroy() {_vcustom.clear();}
	
	union {
		int _vint;
		llong _vllong;
		float _vfloat;
		double _vdouble;
		ldouble _vldouble;
		uchar _vraw[__PIVARIANT_UNION_SIZE__];
		/*complexd _vcomplexd;
		complexld _vcomplexld;
		PITime _vtime;
		PIDate _vdate;
		PIDateTime _vdatetime;
		PISystemTime _vsystime;*/
	};
	PIBitArray _vbitarray;
	PIByteArray _vbytearray;
	PIString _vstring;
	PIStringList _vstringlist;
	PIByteArray _vcustom;
	PIVariant::Type type_;
	
};

template<> inline bool PIVariant::toValue() const {return toBool();}
template<> inline char PIVariant::toValue() const {return (char)toInt();}
template<> inline uchar PIVariant::toValue() const {return (uchar)toInt();}
template<> inline short PIVariant::toValue() const {return (short)toInt();}
template<> inline ushort PIVariant::toValue() const {return (ushort)toInt();}
template<> inline int PIVariant::toValue() const {return toInt();}
template<> inline uint PIVariant::toValue() const {return (uint)toInt();}
template<> inline long PIVariant::toValue() const {return (long)toInt();}
template<> inline ulong PIVariant::toValue() const {return (ulong)toInt();}
template<> inline llong PIVariant::toValue() const {return toLLong();}
template<> inline ullong PIVariant::toValue() const {return (ullong)toLLong();}
template<> inline float PIVariant::toValue() const {return toFloat();}
template<> inline double PIVariant::toValue() const {return toDouble();}
template<> inline ldouble PIVariant::toValue() const {return toLDouble();}
template<> inline complexd PIVariant::toValue() const {return toComplexd();}
template<> inline complexld PIVariant::toValue() const {return toComplexld();}
template<> inline void* PIVariant::toValue() const {return (void*)toLLong();}
template<> inline const char* PIVariant::toValue() const {return toString().data();}
template<> inline PITime PIVariant::toValue() const {return toTime();}
template<> inline PIDate PIVariant::toValue() const {return toDate();}
template<> inline PIDateTime PIVariant::toValue() const {return toDateTime();}
template<> inline PIString PIVariant::toValue() const {return toString();}
template<> inline PIStringList PIVariant::toValue() const {return toStringList();}
template<> inline PIBitArray PIVariant::toValue() const {return toBitArray();}
template<> inline PIByteArray PIVariant::toValue() const {return toByteArray();}

//template<> inline PIVariant PIVariant::fromValue(const char * v) {return PIVariant(PIString(v));}
template<> inline PIVariant PIVariant::fromValue(const bool & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const char & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const uchar & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const short & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const ushort & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const int & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const uint & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const long & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const ulong & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const llong & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const ullong & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const float & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const double & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const ldouble & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const complexd & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const complexld & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIBitArray & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIByteArray & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIString & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIStringList & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PITime & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIDate & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PIDateTime & v) {return PIVariant(v);}
template<> inline PIVariant PIVariant::fromValue(const PISystemTime & v) {return PIVariant(v);}

inline PICout operator <<(PICout s, const PIVariant & v) {
	s.space(); s.setControl(0, true);
	s << "PIVariant(" << PIVariant::typeName(v.type()) << ", ";
	if (v.type() == PIVariant::Custom) s << v._vcustom.size() << " bytes";
	else s << v.toString();
	s << ")";
	s.restoreControl(); return s;
}


#endif // PIVARIANT_H
