#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H

#include <iostream>
#include <complex>

#include <stdint.h>

class Serializable
{
	public:
		virtual ~Serializable()
		{
		}
		virtual void Serialize(std::ostream &stream) const = 0;
		virtual void Unserialize(std::istream &stream) = 0;
		
		template<typename T>
		static void SerializeToUInt64(std::ostream &stream, T value)
		{
			uint64_t val64t = value;
			stream.write(reinterpret_cast<char *>(&val64t), sizeof(val64t));
		}
		
		template<typename T>
		static void SerializeToUInt32(std::ostream &stream, T value)
		{
			uint32_t val32t = value;
			stream.write(reinterpret_cast<char *>(&val32t), sizeof(val32t));
		}
		
		static void SerializeToFloat(std::ostream &stream, float value)
		{
			stream.write(reinterpret_cast<char *>(&value), sizeof(value));
		}
		
		static void SerializeToDouble(std::ostream &stream, double value)
		{
			stream.write(reinterpret_cast<char *>(&value), sizeof(value));
		}
		
		static void SerializeToLDouble(std::ostream &stream, long double value)
		{
			stream.write(reinterpret_cast<char *>(&value), sizeof(value));
		}
		
		static void SerializeToLDoubleC(std::ostream &stream, std::complex<long double> value)
		{
			stream.write(reinterpret_cast<char *>(&value), sizeof(value));
		}
		
		static void SerializeToString(std::ostream &stream, const std::string &str)
		{
			SerializeToUInt64(stream, str.size());
			stream.write(str.c_str(), str.size());
		}
		
		static uint64_t UnserializeUInt64(std::istream &stream)
		{
			return Unserialize<uint64_t>(stream);
		}
		
		static uint32_t UnserializeUInt32(std::istream &stream)
		{
			return Unserialize<uint32_t>(stream);
		}
		
		static double UnserializeFloat(std::istream &stream)
		{
			return Unserialize<float>(stream);
		}

		static double UnserializeDouble(std::istream &stream)
		{
			return Unserialize<double>(stream);
		}

		static long double UnserializeLDouble(std::istream &stream)
		{
			return Unserialize<long double>(stream);
		}
		
		static std::complex<long double> UnserializeLDoubleC(std::istream &stream)
		{
			return Unserialize<std::complex<long double> >(stream);
		}
		
		static void UnserializeString(std::istream &stream, std::string &destStr)
		{
			size_t size = UnserializeUInt64(stream);
			char *str = new char[size];
			stream.read(str, size);
			destStr = std::string(str, size);
			delete[] str;
		}
		
		static std::string UnserializeString(std::istream &stream)
		{
			size_t size = UnserializeUInt64(stream);
			std::string result;
			if(size)
			{
				char *str = new char[size];
				stream.read(str, size);
				result = std::string(str, size);
				delete[] str;
			}
			return result;
		}
	private:
		template<typename T>
		static T Unserialize(std::istream &stream)
		{
			T val;
			stream.read(reinterpret_cast<char *>(&val), sizeof(val));
			return val;
		}
};

#endif
