/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CVariant.h  -  description
                             -------------------
    begin                : Tue Dec 14 1999
    copyright            : (C) 1999-2005 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 <string>

/// @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, large or corresponding to BLOBS in database
   VAR_BUFFER    = 32,  ///< Data pointer, large or 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()                     { return m_null; }
};

/// @}

#endif
