/* 
   This file is part of libodbc++.
   
   Copyright (C) 1999 Manush Dodunekov <manush@litecom.net>
   
   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; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#ifndef __ODBCXX_TYPES_H
#define __ODBCXX_TYPES_H

#include <odbc++/setup.h>

#include <exception>
#include <string>
#include <ctime>

#if defined(ODBCXX_HAVE_ISQL_H) && defined(ODBCXX_HAVE_ISQLEXT_H)
# include <isql.h>
# include <isqlext.h>
#elif defined(ODBCXX_HAVE_SQL_H) && defined(ODBCXX_HAVE_SQLEXT_H)
# include <sql.h>
# include <sqlext.h>
#else
# error "Whoops. Can not recognize the ODBC subsystem."
#endif

// fixups for current iODBC, which kindly doesn't provide SQL_TRUE and
// SQL_FALSE macros

#if !defined(SQL_TRUE)
# define SQL_TRUE 1
#endif

#if !defined(SQL_FALSE)
# define SQL_FALSE 0
#endif


// Setup our ODBC3_C (odbc3 conditional) macro
#if ODBCVER >= 0x0300

# define ODBC3_C(odbc3_value,old_value) odbc3_value

#else

# define ODBC3_C(odbc3_value,old_value) old_value

#endif


// ODBC3_DC (odbc3 dynamic conditional)
// Every context using this macro should provide
// a this->_getDriverInfo() method returning
// a const DriverInfo*

#if ODBCVER >= 0x0300

# define ODBC3_DC(odbc3_value,old_value) \
(this->_getDriverInfo()->getMajorVersion()>=3?odbc3_value:old_value)

#else

# define ODBC3_DC(odbc3_value,old_value) old_value

#endif




#if defined(ODBCXX_HAVE_INTTYPES_H)
# include <inttypes.h>
#endif

#include <vector>


namespace odbc {

  // We want Long to be at least 64 bits

#if defined(WIN32)
  
  typedef __int64 Long;

#elif defined(ODBCXX_HAVE_INTTYPES_H)

  typedef int64_t Long;

#else

# if ODBCXX_SIZEOF_INT == 8

  typedef int Long;

# elif ODBCXX_SIZEOF_LONG == 8
  
  typedef long Long;

# elif ODBCXX_SIZEOF_LONG_LONG == 8

  typedef long long Long;

# else
  
#  error "Can't find an appropriate at-least-64-bit integer"

# endif

#endif


  //constants:
  //how much we try to fetch with each SQLGetData call
  const int GETDATA_CHUNK_SIZE=4*1024;
  //how much we write with each SQLPutData call
  const int PUTDATA_CHUNK_SIZE=GETDATA_CHUNK_SIZE;

  //how much we read/write in string<->stream conversion
  //better names for those?
  const int STRING_TO_STREAM_CHUNK_SIZE=1024;
  const int STREAM_TO_STRING_CHUNK_SIZE=STRING_TO_STREAM_CHUNK_SIZE;





  /** SQL type constants
   */
  struct Types {
    /** Type constants
     */
    enum SQLType {
      /** An SQL BIGINT */
      BIGINT		= SQL_BIGINT, 
      /** An SQL BINARY (fixed length) */
      BINARY		= SQL_BINARY,
      /** An SQL BIT */
      BIT		= SQL_BIT,
      /** An SQL CHAR (fixed length) */
      CHAR		= SQL_CHAR,
      /** An SQL DATE */
      DATE		= ODBC3_C(SQL_TYPE_DATE,SQL_DATE),
      /** An SQL DECIMAL (precision,scale) */
      DECIMAL		= SQL_DECIMAL,
      /** An SQL DOUBLE */
      DOUBLE		= SQL_DOUBLE,
      /** An SQL FLOAT */
      FLOAT		= SQL_FLOAT,
      /** An SQL INTEGER */
      INTEGER		= SQL_INTEGER,
      /** An SQL LONGVARBINARY (variable length, huge) */
      LONGVARBINARY	= SQL_LONGVARBINARY,
      /** An SQL LONGVARCHAR (variable length, huge) */
      LONGVARCHAR	= SQL_LONGVARCHAR,
      /** An SQL NUMERIC (precision,scale) */
      NUMERIC		= SQL_NUMERIC,
      /** An SQL REAL */
      REAL		= SQL_REAL,
      /** An SQL SMALLINT */
      SMALLINT		= SQL_SMALLINT,
      /** An SQL TIME */
      TIME		= ODBC3_C(SQL_TYPE_TIME,SQL_TIME),
      /** An SQL TIMESTAMP */
      TIMESTAMP		= ODBC3_C(SQL_TYPE_TIMESTAMP,SQL_TIMESTAMP),
      /** An SQL TINYINT */
      TINYINT		= SQL_TINYINT,
      /** An SQL VARBINARY (variable length less than 256) */
      VARBINARY		= SQL_VARBINARY,
      /** An SQL VARCHAR (variable length less than 256) */
      VARCHAR		= SQL_VARCHAR
    };
  };



  /** A chunk of bytes. 
   * 
   *  Used for setting and getting binary values.
   */
  class ODBCXX_EXPORT Bytes {
  private:
    signed char* buf_;
    size_t len_;

    //no copying
    Bytes(const Bytes&);
    Bytes& operator=(const Bytes&);

  public:
    /** Constructor */
    Bytes(const signed char* data, size_t dataLen)
      :buf_(NULL),len_(dataLen) {
      if(len_>0) {
	buf_=new signed char[len_];
	memcpy(buf_,data,len_);
      }
    }
    
    /** Destructor */
    ~Bytes() { 
      delete[] buf_; 
    }

    /** Returns a pointer to the data */
    const signed char* getData() const { 
      return buf_;
    }

    /** Returns the size of the data */
    size_t getSize() const {
      return len_;
    }
  };


  /** An SQL DATE */
  class ODBCXX_EXPORT Date {
  protected:
    int year_;
    int month_;
    int day_;

    virtual void _invalid(const char* what, int value);

    int _validateYear(int y) {
      return y;
    }
    
    int _validateMonth(int m) {
      if(m<1 || m>12) {
	this->_invalid("month",m);
      }
      return m;
    }
    
    int _validateDay(int d) {
      if(d<1 || d>31) {
	this->_invalid("day",d);
      }
      return d;
    }

  public:
    /** Constructor. 
     */
    Date(int year, int month, int day) {
      this->setYear(year);
      this->setMonth(month);
      this->setDay(day);
    }

    /** Constructor. 
     * 
     * Sets this date to today.
     */
    explicit Date();

    /** Constructor.
     * 
     * Sets this date to the specified time_t value.
     */
    Date(time_t t) {
      this->setTime(t);
    }

    /** Constructor.
     * 
     * Sets this date to the specified string in the <tt>YYYY-MM-DD</tt> format.
     */
    Date(const std::string& str) {
      this->parse(str);
    }

    /** Copy constructor */
    Date(const Date& d)
      :year_(d.year_),
       month_(d.month_),
       day_(d.day_) {}

    /** Assignment operator */
    Date& operator=(const Date& d) {
      year_=d.year_;
      month_=d.month_;
      day_=d.day_;
      return *this;
    }

    /** Destructor */
    virtual ~Date() {}

    /** Sets this date to the specified time_t value */
    virtual void setTime(time_t t);

    /** Returns the time_t value of <tt>00:00:00</tt> at this date */
    time_t getTime() const;

    /** Sets this date from a string in the <tt>YYYY-MM-DD</tt> format */
    void parse(const std::string& str);

    /** Gets the year of this date */
    int getYear() const {
      return year_;
    }

    /** Gets the month of this date */
    int getMonth() const {
      return month_;
    }

    /** Gets the monthday of this date */
    int getDay() const {
      return day_;
    }

    /** Sets the year of this date */
    void setYear(int year) {
      year_=this->_validateYear(year);
    }

    /** Sets the month of this date */
    void setMonth(int month) {
      month_=this->_validateMonth(month);
    }

    /** Sets the day of this date */
    void setDay(int day) {
      day_=this->_validateDay(day);
    }

    /** Gets the date as a string in the <tt>YYYY-MM-DD</tt> format */
    virtual std::string toString() const;
  };

  /** An SQL TIME */
  class ODBCXX_EXPORT Time {
  protected:
    int hour_;
    int minute_;
    int second_;

    virtual void _invalid(const char* what, int value);

    int _validateHour(int h) {
      if(h<0 || h>23) {
	this->_invalid("hour",h);
      }
      return h;
    }

    int _validateMinute(int m) {
      if(m<0 || m>59) {
	this->_invalid("minute",m);
      }
      return m;
    }

    int _validateSecond(int s) {
      if(s<0 || s>61) {
	this->_invalid("second",s);
      }
      return s;
    }

  public:
    /** Constructor */
    Time(int hour, int minute, int second) {
      this->setHour(hour);
      this->setMinute(minute);
      this->setSecond(second);
    }

    /** Constructor.
     * 
     * Sets the time to now.
     */
    explicit Time();

    /** Constructor.
     * 
     * Sets the time to the specified <tt>time_t</tt> value.
     */
    Time(time_t t) {
      this->setTime(t);
    }

    /** Constructor.
     * 
     * Sets the time to the specified string in the <tt>HH:MM:SS</tt> format.
     */
    Time(const std::string& str) {
      this->parse(str);
    }

    /** Copy constructor */
    Time(const Time& t)
      :hour_(t.hour_),
       minute_(t.minute_),
       second_(t.second_) {}

    /** Assignment operator */
    Time& operator=(const Time& t) {
      hour_=t.hour_;
      minute_=t.minute_;
      second_=t.second_;
      return *this;
    }

    /** Destructor */
    virtual ~Time() {}

    /** Sets the time to the specified <tt>time_t</tt> value */
    virtual void setTime(time_t t);

    /** Returns the <tt>time_t</tt> value of <tt>1970-01-01</tt> at this time */
    time_t getTime() const;

    /** Sets this time from a string in the <tt>HH:MM:SS</tt> format */
    void parse(const std::string& str);

    /** Gets the hour of this time */
    int getHour() const {
      return hour_;
    }

    /** Gets the minute of this time */
    int getMinute() const {
      return minute_;
    }

    /** Gets the second of this time */
    int getSecond() const {
      return second_;
    }

    /** Sets the hour of this time */
    void setHour(int h) {
      hour_=this->_validateHour(h);
    }

    /** Sets the minute of this time */
    void setMinute(int m) {
      minute_=this->_validateMinute(m);
    }

    /** Sets the second of this time */
    void setSecond(int s) {
      second_=this->_validateSecond(s);
    }

    virtual std::string toString() const;
  };


  /** An SQL TIMESTAMP 
   */
  class ODBCXX_EXPORT Timestamp : public Date, public Time {
  private:
    int nanos_;
    
    virtual void _invalid(const char* what, int value);

    int _validateNanos(int n) {
      if(n<0) {
	this->_invalid("nanoseconds",n);
      }
      return n;
    }

  public:
    /** Constructor */
    Timestamp(int year, int month, int day,
	      int hour, int minute, int second,
	      int nanos =0)
      :Date(year,month,day), Time(hour,minute,second) {
      this->setNanos(nanos);
    }

    /** Constructor.
     *
     * Sets the timestamp to now.
     */
    explicit Timestamp();

    /** Constructor.
     *
     * Sets this timestamp to the specified <tt>time_t</tt> value.
     */
    Timestamp(time_t t) {
      this->setTime(t);
    }

    /** Constructor.
     *
     * Sets this timestamp from a <tt>YYYY-MM-DD HH:MM:SS[.NNN...]</tt> format
     */
    Timestamp(const std::string& s) {
      this->parse(s);
    }


    /** Copy constructor */
    Timestamp(const Timestamp& t)
      :Date(t),Time(t),nanos_(t.nanos_) {}

    /** Assignment operator */
    Timestamp& operator=(const Timestamp& t) {
      Date::operator=(t);
      Time::operator=(t);
      nanos_=t.nanos_;
      return *this;
    }

    /** Destructor */
    virtual ~Timestamp() {}

    /** Sets this timestamp to the specified <tt>time_t</tt> value */
    virtual void setTime(time_t t);
    
    /** Gets the time_t value of this timestamp */
    virtual time_t getTime() {
      return Date::getTime()+Time::getTime();
    }

    /** Set this timestamp from a <tt>YYYY-MM-DD HH:MM:SS[.NNN...]</tt> format
     */
    void parse(const std::string& s);

    /** Gets the nanoseconds value of this timestamp */
    int getNanos() const {
      return nanos_;
    }

    /** Sets the nanoseconds value of this timestamp */
    void setNanos(int nanos) {
      nanos_=this->_validateNanos(nanos);
    }

    virtual std::string toString() const;
  };


  //this is used for several 'lists of stuff' below
  //expects T to be a pointer-to-something, and
  //the contents will get deleted when the vector 
  //itself is deleted
  template <class T> class ODBCXX_EXPORT CleanVector : public std::vector<T> {
  private:
    CleanVector(const CleanVector<T>&); //forbid
    CleanVector<T>& operator=(const CleanVector<T>&); //forbid

  public:
    explicit CleanVector() {}
    virtual ~CleanVector() {
      while(!this->empty()) {
	typename std::vector<T>::iterator i=this->begin();
	delete *i;
	this->erase(i);
      }
    }
  };


  /** Used internally - represents the result of an SQLError call 
   */
  class ODBCXX_EXPORT DriverMessage {
    friend class ErrorHandler;

  private:
    char state_[SQL_SQLSTATE_SIZE+1];
    char description_[SQL_MAX_MESSAGE_LENGTH];
    SQLINTEGER nativeCode_;

    DriverMessage() {}

  public:
    virtual ~DriverMessage() {}

    const char* getSQLState() const {
      return state_;
    }

    const char* getDescription() const {
      return description_;
    }

    int getNativeCode() const {
      return nativeCode_;
    }
  };

  
  /** The exception thrown when errors occur inside the library.
   */
  class SQLException : public std::exception {
  private:
    std::string reason_;
    std::string sqlState_;
    int errorCode_;
    
  public:
    /** Constructor */
    SQLException(std::string reason ="", 
		 std::string sqlState ="",
		 int vendorCode =0)
      :reason_(reason), 
       sqlState_(sqlState),
       errorCode_(vendorCode) {}

    /** Copy from a DriverMessage */
    SQLException(const DriverMessage& dm)
      :reason_(dm.getDescription()),
       sqlState_(dm.getSQLState()),
       errorCode_(dm.getNativeCode()) {}

    /** Destructor */
    virtual ~SQLException() {}

    /** Get the vendor error code of this exception */
    int getErrorCode() const {
      return errorCode_;
    }
    
    /** Gets the SQLSTATE of this exception. 
     * 
     * Consult your local ODBC reference for SQLSTATE values.
     */
    const std::string& getSQLState() const {
      return sqlState_;
    }
    
    /** Gets the description of this message */
    const std::string& getMessage() const {
      return reason_;
    }


    /** Gets the description of this message */
    virtual const char* what() const {
      return reason_.c_str();
    }
  };


  /** Represents an SQL warning.
   * 
   * Contains the same info as an SQLException.
   */
  class SQLWarning : public SQLException {

    SQLWarning(const SQLWarning&); //forbid
    SQLWarning& operator=(const SQLWarning&); //forbid

  public:
    /** Constructor */
    SQLWarning(std::string reason ="",
	       std::string sqlState ="",
	       int vendorCode =0)
      :SQLException(reason,sqlState,vendorCode) {}
    
    /** Copy from a DriverMessage */
    SQLWarning(const DriverMessage& dm)
      :SQLException(dm) {}
    
    /** Destructor */
    virtual ~SQLWarning() {}
  };

  typedef CleanVector<SQLWarning*> WarningList;
  

  template <class T> class Deleter {
  private:
    T* ptr_;
    bool isArray_;

    Deleter(const Deleter<T>&);
    Deleter<T>& operator=(const Deleter<T>&);

  public:
    explicit Deleter(T* ptr, bool isArray =false) 
      :ptr_(ptr), isArray_(isArray) {}
    ~Deleter() {
      if(!isArray_) {
	delete ptr_;
      } else {
	delete[] ptr_;
      }
    }
  };

}; // namespace odbc


#endif // __ODBCXX_TYPES_H
