/*	Copyright  (c)	Günter Woigk 2010 - 2018
					mailto:kio@little-bat.de

	This file is free software

 	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.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	• Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
	• Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
	OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
	OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#ifndef VALUE_H
#define VALUE_H

#include "Libraries/kio/kio.h"
#include "Libraries/Templates/Array.h"
#include "IdfID.h"
#include "Type.h"
#include "globals.h"

class Value; typedef class Value const cValue;


class Value
{
	// Values combine a value with a type.
	// Supported types are: numeric (basic types and enum) and arrays incl. cstrings.
	// Values with a signed integer type are guaranteed to fit in int64.
	// Values with a unsigned integer type are guaranteed to fit in uint64.
	// Supported operators return a new instance of Value.
	// Most operators strip ENUM.
	// The result of pure integer operations is clamped to int64 or uint64 as appropriate.
	// The result type of pure integer operations is determined from signedness and result size.
	// The result type of floating point operations is the largest floating point type involved.

public:
	cType* type;
	uint64 dummy_padding = 0;

	union  // value
	{
		float128	  value;
		Array<uint8>  u8array;
		Array<int8>   i8array;
		Array<uint16> u16array;
		Array<int16>  i16array;
		Array<uint32> u32array;
		Array<int32>  i32array;
		Array<int64>  i64array;
		Array<uint64> u64array;
		Array<float32> f32array;
		Array<float64> f64array;
		Array<float128> f128array;
	};

	Value (cType* t)				noexcept :type(t),value(0) {}
	Value (cType* t, float128 n)	noexcept;	// value of integer types must fit in u/long
	Value (cstr utf8string)			noexcept;	// cstrings: sets errno for utf8_to_ucs1/2 conversion
	Value (int64 n)					noexcept;	// nothrow even if value exceeds long
	Value (uint64 n)				noexcept;	// nothrow even if value exceeds ulong
	Value (cType*, Array<uint8>)	noexcept;
	Value (cType*, Array<int8>)		noexcept;
	Value (cType*, Array<uint16>)	noexcept;
	Value (cType*, Array<int16>)	noexcept;
	Value (cType*, Array<uint32>)	noexcept;
	Value (cType*, Array<int32>)	noexcept;
	Value (cType*, Array<uint64>)	noexcept;
	Value (cType*, Array<int64>)	noexcept;
	Value (cType*, Array<float32>)	noexcept;
	Value (cType*, Array<float64>)	noexcept;
	Value (cType*, Array<float128>)	noexcept;
	~Value();
	Value (Value const&);
	Value (Value&&);
	Value& operator= (Value const&);
	Value& operator= (Value&&);

	bool isNumeric		()	const	{ return type->isNumeric(); }
	bool isInteger		()	const	{ return type->isInteger(); }
	bool isSignedInt	()	const	{ return type->isSignedInt(); }
	bool isUnsignedInt	()	const	{ return type->isUnsignedInt(); }
	//bool isInteger32	()	const	{ return type->isInteger() && type->bits == 32; }
	//bool isInteger64	()	const	{ return type->isInteger() && type->bits == 64; }
	bool isFloat		()	const	{ return type->isFloat(); }
	bool isString		()	const	{ return type->isString(); }
	bool isArray		()	const	{ return type->isArray(); }
	//bool isPtr  		()	const	{ return type->isPtr(); }

	bool is0			()	const	{ assert(isNumeric()); return value == 0.0l; }
	bool isnot0			()	const	{ assert(isNumeric()); return value != 0.0l; }
	bool operator!=		(float128 n) const	{ assert(isNumeric()); return value != n; }
	bool operator==		(float128 n) const	{ assert(isNumeric()); return value == n; }
	bool operator>		(float128 n) const	{ assert(isNumeric()); return value >  n; }
	bool operator<		(float128 n) const	{ assert(isNumeric()); return value <  n; }
	bool operator>=		(float128 n) const	{ assert(isNumeric()); return value >= n; }
	bool operator<=		(float128 n) const	{ assert(isNumeric()); return value <= n; }

	Value	cast		(cType* zt) const;

	int8	i8Value		()	const	{ assert(isNumeric()); assert(value ==  int8 (value));	return  int8 (value); }
	uint8	u8Value		()	const	{ assert(isNumeric()); assert(value == uint8 (value));	return uint8 (value); }
	int16	i16Value	()	const	{ assert(isNumeric()); assert(value ==  int16(value));	return  int16(value); }
	uint16	u16Value	()	const	{ assert(isNumeric()); assert(value == uint16(value));	return uint16(value); }
	int32	i32Value	()	const	{ assert(isNumeric()); assert(value ==  int32(value));	return  int32(value); }
	uint32	u32Value	()	const	{ assert(isNumeric()); assert(value == uint32(value));	return uint32(value); }
	int64	i64Value	()	const	{ assert(isNumeric()); assert(value ==  int64(value));	return  int64(value); }
	uint64	u64Value	()	const	{ assert(isNumeric()); assert(value == uint64(value));	return uint64(value); }
	float32	f32Value	()	const	{ assert(isNumeric()); return float32(value); }
	float64	f64Value	()	const	{ assert(isNumeric()); return float64(value); }
	float128 f128Value	()	const	{ assert(isNumeric()); return value; }
	float128 getValue	()	const	{ assert(isNumeric()); return value; }
	cstr	getString	()	const;

	Array<uint8>	const& rawArray () const { assert(isArray()); return u8array; } // array.count() as for actual type!
	Array<int8>		const& i8Array  () const { assert(isArray()&&type->basetype->stripEnum()==target->tint8);   return i8array; }
	Array<uint8>	const& u8Array  () const { assert(isArray()&&type->basetype->stripEnum()==target->tuint8);  return u8array; }
	Array<int16>	const& i16Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tint16);  return i16array; }
	Array<uint16>	const& u16Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tuint16); return u16array; }
	Array<int32>	const& i32Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tint32);  return i32array; }
	Array<uint32>	const& u32Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tuint32); return u32array; }
	Array<int64>	const& i64Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tint64);  return i64array; }
	Array<uint64>	const& u64Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tuint64); return u64array; }
	Array<float32>	const& f32Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tsfloat); return f32array; }
	Array<float64>	const& f64Array () const { assert(isArray()&&type->basetype->stripEnum()==target->tfloat);  return f64array; }
	Array<float128>	const& f128Array() const { assert(isArray()&&type->basetype->stripEnum()==target->tlfloat); return f128array;}

	int64	rawInt64	()	const	{ assert(isNumeric()); return value<0 ? int64(value) : int64(uint64(value)); }
	uint64	rawUInt64	()	const	{ assert(isNumeric()); return value<0 ? uint64(int64(value)) : uint64(value); }
//	int64	rawFloat64	()	const	{ assert(isNumeric()); float64 f = float64(f128);	return *(int64*)&f; }
//	int64	raw64Value	()	const	{ assert(isNumeric()); return isFloat() ? rawFloat64() : rawInt64(); }

	Value operator+		(cValue&)	const;
	Value operator-		(cValue&)	const;
	Value operator*		(cValue&)	const;
	Value operator/		(cValue&)	const;
	Value operator%		(cValue&)	const;
	Value operator&		(cValue&)	const;
	Value operator|		(cValue&)	const;
	Value operator^		(cValue&)	const;
	Value operator>>	(cValue&)	const;
	Value operator<<	(cValue&)	const;
	Value operator==	(cValue&)	const;
	Value operator!=	(cValue&)	const;
	Value operator>		(cValue&)	const;
	Value operator<		(cValue&)	const;
	Value operator>=	(cValue&)	const;
	Value operator<=	(cValue&)	const;
	Value operator!		()			const;
	Value operator~		()			const;
	Value operator+		()			const;
	Value operator-		()			const;
	//Value operator++	(int);
	//void	operator*=	(uint32 n)	{ assert(isNumeric()); f128 *= n; }

	cstr toString		()	const;

	void serialize		(FD&) const throws;
	void deserialize	(FD&) throws;
};

extern Value eval (cValue&, IdfID op, cValue&);
inline cstr tostr(cValue& value) { return value.toString(); }


#if 0
class Data
{
	cstr		TypeName	(bool full)	const;
};


extern Data		BestIntData		(cType* minsz, int64 n);
extern Data		BestUintData	(cType* minsz, uint64 n);
//extern cType* 	MinUintType		(uint64);
//extern cType* 	MinIntType		(int64);

inline 	cstr 	Data::TypeName		(bool full)	const		{ return type->Name(full); }
#endif



#endif





