/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CVariant.h  -  description
                             -------------------
    begin                : Tue Dec 14 1999
    copyright            : (C) 1999-2006 by Alexey Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

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

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CVARIANT_H__
#define __CVARIANT_H__

#include <sptk3/CDateTime.h>
#include <sptk3/CBuffer.h>
#include <sptk3/CException.h>
#include <sptk3/cxml>

namespace sptk {

   /// @addtogroup utility Utility Classes
   /// @{

   /// Variant types
	enum CVariantType { 
		VAR_NONE      = 0,   ///< Undefined
		VAR_INT       = 1,   ///< Integer
		VAR_FLOAT     = 2,   ///< Floating-point (double)
		VAR_MONEY     = 4,   ///< Floating-point (double) money
		VAR_STRING    = 8,   ///< String pointer
		VAR_TEXT      = 16,  ///< String pointer, corresponding to BLOBS in database
		VAR_BUFFER    = 32,  ///< Data pointer, corresponding to BLOBS in database
		VAR_DATE      = 64,  ///< CDateTime (double)
		VAR_DATE_TIME = 128, ///< CDateTime (double)
		VAR_IMAGE_PTR = 256  ///< Image pointer
	};

	class CField;

   /// @brief Variant data buffer (internal).
   ///
   /// A buffer for data with the variable length like strings, or just generic buffers
	struct CVariantDataBuffer {
		char *   data;         ///< String or buffer pointer
		unsigned size;         ///< Allocated buffer size
	};

   /// @brief Universal data storage. 
   ///
   /// It's not too compact or too fast.
   /// It just allows to pass the data of different structure through the same 
   /// parameter.
	class SP_EXPORT CVariant {
	protected:
      /// Internal variant data storage
		union variantData {
			long      intData;         ///< Integer data
			double    floatData;       ///< Floating point data
			double    timeData;        ///< CDateTime data
			CVariantDataBuffer buffer; ///< A buffer for data with the variable length like strings, or just generic buffers
			void *    imagePtr;        ///< Image pointer
		} m_data;                     ///< Data storage union
		unsigned     m_dataSize;      ///< Data size 
		short        m_dataType;      ///< Data type
		short        m_null;          ///< Null value flag.

      /// Copies data from another CVariant
		void setData(const CVariant& C);

      /// Releases allocated buffer (if any)
		void releaseBuffers();

	protected:	
      /// @brief Sets the data size
      ///
      /// If data size is 0, the NULL flag is set. Otherwise, the NULL flag is set to false.
      /// @param ds unsigned, data size (in bytes).
		void dataSize(unsigned ds)        { m_dataSize = ds;  m_null = (m_dataSize == 0); }

      /// Sets the data type
		void dataType(CVariantType dt)    { m_dataType = dt;  }

	public:

      /// Constructor
		CVariant()                                  { m_dataType = VAR_NONE;  m_data.intData = 0; m_null = true; }

      /// Constructor
		CVariant(int value)                         { m_dataType = VAR_INT;   m_data.intData = value; m_null = false; }

      /// Constructor
		CVariant(unsigned int value)                { m_dataType = VAR_INT;   m_data.intData = value; m_null = false; }

      /// Constructor
		CVariant(long value)                        { m_dataType = VAR_INT;   m_data.intData = value; m_null = false; }

      /// Constructor
		CVariant(unsigned long value)               { m_dataType = VAR_INT;   m_data.intData = value; m_null = false; }

      /// Constructor
		CVariant(float value)                       { m_dataType = VAR_FLOAT; m_data.floatData = value; m_null = false; }

      /// Constructor
		CVariant(double value)                      { m_dataType = VAR_FLOAT; m_data.floatData = value; m_null = false; }

      /// Constructor
		CVariant(const char * value)                { m_dataType = VAR_NONE;  setString(value); }

      /// Constructor
		CVariant(const std::string& v)              { m_dataType = VAR_NONE;  setString(v.c_str(),v.length()); }

      /// Constructor
		CVariant(CDateTime v)                       { m_dataType = VAR_DATE_TIME; m_data.timeData = v; m_null = false; }

      /// Constructor
		CVariant(const void * value,int sz)         { m_dataType = VAR_NONE;  setBuffer(value,sz); }

      /// Constructor
		CVariant(const CBuffer& value)              { m_dataType = VAR_NONE;  setBuffer(value.data(),value.bytes()); }

      /// Constructor
		CVariant(const CVariant& value)             { m_dataType = VAR_NONE;  setData(value); }

      /// Destructor
		virtual ~CVariant() { releaseBuffers(); }

      /// Assignment method
		void setInteger(long value);

      /// Assignment method
		void setFloat(double value);

      /// Assignment method
		void setMoney(double value);

      /// Assignment method
		void setString(const char * value,int maxlen=0);

      /// Assignment method
		void setString(const std::string& value) { setString(value.c_str(),value.length()); }

      /// Assignment method
		void setText(const char * value);

      /// Assignment method
		void setText(const std::string& str);

      /// Assignment method
		void setBuffer(const void * value,int sz);

      /// Assignment method
		void setBuffer(const std::string& str);

      /// Assignment method
		void setDate(CDateTime value);

      /// Assignment method
		void setDateTime(CDateTime value);

      /// Assignment method
		void setImagePtr(const void *value);

      /// Assignment operator
		CVariant& operator = (const CVariant &C)    { if (this == &C) return *this; setData(C); return *this; };

      /// Assignment operator
		CVariant& operator = (int value)            { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (long value)           { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (unsigned int value)   { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (unsigned long value)  { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (short value)          { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (unsigned short value) { setInteger(value); return *this;  }

      /// Assignment operator
		CVariant& operator = (float value)          { setFloat(value); return *this;    }

      /// Assignment operator
		CVariant& operator = (double value)         { setFloat(value); return *this;    }

      /// Assignment operator
		CVariant& operator = (const char * value)   { setString(value); return *this;   }

      /// Assignment operator
		CVariant& operator = (const std::string& value) { setString(value.c_str(),value.length()); return *this; }

      /// Assignment operator
		CVariant& operator = (CDateTime value)      { setDateTime(value); return *this; }

      /// Assignment operator
		CVariant& operator = (const void *value)    { setImagePtr(value); return *this; }

      /// Assignment operator
		CVariant& operator = (const CBuffer& value) { setBuffer(value.data(),value.bytes()); return *this; }

      /// Directly reads the internal data
		long         getInteger() const;

      /// Directly reads the internal data
		double       getFloat() const;

      /// Directly reads the internal data
		const char * getString() const;

      /// Directly reads the internal data
		const char * getText() const;

      /// Directly reads the internal data
		const void * getBuffer() const;

      /// Directly reads the internal data
		CDateTime    getDateTime() const;

      /// Directly reads the internal data
		CDateTime    getDate() const;

      /// Directly reads the internal data
		void *       getImagePtr() const;

      /// Returns the data type
		CVariantType dataType() const     { return (CVariantType) m_dataType;    }

      /// Returns the data size
		unsigned dataSize() const         { return m_dataSize;                   }

      /// Returns the allocated buffer size
		unsigned bufferSize() const       { return m_data.buffer.size;     }

      /// Returns the internal buffer 
		void * dataBuffer() const         { return (void *)(variantData *)&m_data; }

      /// Conversion operator
		operator bool () const throw(CException)           { return asBool();               }

      /// Conversion operator
		operator int () const throw(CException)            { return asInteger();            }

      /// Conversion operator
		operator unsigned () const throw(CException)       { return asInteger();            }

      /// Conversion operator
		operator long () const throw(CException)           { return asInteger();            }

      /// Conversion operator
		operator unsigned long () const  throw(CException) { return asInteger();            }

      /// Conversion operator
		operator float () const throw(CException)          { return (float)asFloat();       }

      /// Conversion operator
		operator double () const throw(CException)         { return asFloat();              }

      /// Conversion operator
		operator std::string () const throw(CException)    { return asString();             }

      /// Conversion operator
		operator CDateTime () const throw(CException)      { return asDateTime();           }

      /// @brief Conversion method
      ///
      /// Converts variant value to double.
		long      asInteger() const throw(CException);

      /// @brief Conversion to bool
      ///
      /// Converts variant string value with first char one of 'Y','y','T','t' to true,
      /// and one of 'N','n','F','f' to false. 
      /// For the integer and float values, the value <=0 is false, and > 0 is true.
		bool      asBool() const throw(CException);

      /// @brief Conversion to double
      ///
      /// Converts variant value to double.
		double    asFloat() const throw(CException);

      /// @brief Conversion to string 
      ///
      /// Converts variant value to string.
		virtual std::string asString() const throw(CException);

      /// @brief Conversion method
      ///
      /// Converts variant value to CDateTime. The time part of CDdatetime is empty.
		CDateTime asDate() const throw(CException);

      /// @brief Conversion method
      ///
      /// Converts variant value to CDateTime.
		CDateTime asDateTime() const throw(CException);

      /// @brief Conversion method
      ///
      /// Simply returns the internal data pointer for string/text/blob types.
      /// For incompatible types throws an exception.
		void *asImagePtr() const throw(CException);

      /// @brief Sets the NULL state
      /// 
      /// Useful for the database operations. 
      /// Releases the memory allocated for string/text/blob types.
      /// Retains the data type. Sets the data to zero(s).
		void setNull();

      /// @brief Null flag
      ///
      /// Returns true if the NULL state is set
		bool isNull() const { return m_null != 0; }

      /// @brief Returns a name for a particular variant type
      ///
      /// @param type CVariantType, a variant type
		static std::string typeName(CVariantType type); 

      /// @brief Returns a type for a particular variant type name
      ///
      /// @param name const char*, a variant type name
		static CVariantType nameType(const char* name);

      /// @brief Loads the data from XML node
      ///
      /// @param node const CXmlNode&, XML node to load data from
		void load(const CXmlNode& node);

      /// @brief Loads the data from XML node
      ///
      /// @param node const CXmlNode*, XML node to load data from
		void load(const CXmlNode* node) { load(*node); }

      /// @brief Saves the data into XML node
      ///
      /// @param node CXmlNode&, XML node to save data into
		void save(CXmlNode& node) const;

      /// @brief Saves the data into XML node
      ///
      /// @param node CXmlNode*, XML node to save data into
		void save(CXmlNode* node) const { save(*node); }
	};
   /// @}
}
#endif
