/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CDatabase.h  -  description
                             -------------------
    begin                : Wed Dec 15 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 __CDATABASE_H__
#define __CDATABASE_H__

#include <sptk3/sptk.h>
#include <sptk3/CStrings.h>
#include <sptk3/CWaiter.h>
#include <sptk3/CVariant.h>
#include <sptk3/CFileLog.h>

#include <vector>

namespace sptk {

   /// @addtogroup Database Database Support
   /// @{

	class CQuery;

   /// @brief Database connector
   ///
   /// Implements a thread-safe connection to general database. It is used 
   /// as a base class for the particular database driver, CODBCDatabase,
   /// for instance. 
	class SP_EXPORT CDatabase : public CWaiter {
		typedef std::vector<CQuery *> CQueryVector;
		friend class CQuery;

	protected:

		CQueryVector  m_queryList;      ///< The list of queries that use this database
		std::string   m_connString;     ///< The ODBC driver connection string
		bool          m_inTransaction;  ///< The in-transaction flag
		CFileLog *    m_log;            ///< Log for the database events (optional)
		std::string   m_objectName;     ///< Object name for logs and error messages
		CDateTime     m_lastActive;     ///< The date and time of the last operation on the database

      /// Attaches (links) query to the database
		bool linkQuery(CQuery *q);

      /// Ulinks query from the database
		bool unlinkQuery(CQuery *q);

	protected:
      // These methods get access to CQuery's protected members
		void querySetAutoPrep(CQuery *q,bool pf);       ///< Sets internal CQuery m_autoPrepare flag
		void querySetStmt(CQuery *q,void *stmt);        ///< Sets internal CQuery statement handle
		void querySetConn(CQuery *q,void *conn);        ///< Sets internal CQuery connection handle
		void querySetPrepared(CQuery *q,bool pf);       ///< Sets internal CQuery m_prepared flag
		void querySetActive(CQuery *q,bool af);         ///< Sets internal CQuery m_active flag
		void querySetEof(CQuery *q,bool eof);           ///< Sets internal CQuery m_eof flag

      // These methods implement the actions requested by CQuery
		virtual std::string queryError(const CQuery *query) const; ///< Retrieves an error (if any) after executing a statement
		virtual void queryAllocStmt(CQuery *query);     ///< Allocates an ODBC statement
		virtual void queryFreeStmt(CQuery *query);      ///< Deallocates an ODBC statement
		virtual void queryCloseStmt(CQuery *query);     ///< Closes an ODBC statement
		virtual void queryPrepare(CQuery *query);       ///< Prepares a query if supported by database
		virtual void queryUnprepare(CQuery *query);     ///< Unprepares a query if supported by database
		virtual void queryExecute(CQuery *query);       ///< Executes a statement
		virtual int  queryColCount(CQuery *query);      ///< Counts columns of the dataset (if any) returned by query
		virtual void queryColAttributes(CQuery *query,short column,short descType,long& value); ///< In a dataset returned by a query, retrieves the column attributes
		virtual void queryColAttributes(CQuery *query,short column,short descType,char *buff,int len); ///< In a dataset returned by a query, retrieves the column attributes
		virtual void queryBindParameters(CQuery *query); ///< Binds the parameters to the query
		virtual void queryOpen(CQuery *query);           ///< Opens the query for reading data from the query' recordset
		virtual void queryFetch(CQuery *query);          ///< Reads data from the query' recordset into fields, and advances to the next row. After reading the last row sets the EOF (end of file, or no more data) flag.

	protected:

		std::string m_driverDescription; ///< Driver description is filled by the particular driver.

      /// @brief Constructor
      ///
      /// Protected constructor prevents creating an instance of the
      /// CDatabase. Instead, it is possible to create an instance of derived
      /// classes.
      /// @param connectionString std::string, the connection string
		CDatabase(std::string connectionString);

      /// Stub function to throw an exception in case if the
      /// called method isn't implemented in the derived class
		void notImplemented(const char *methodName) const;

		void *queryHandle(CQuery *query) const;          ///< Retrieves internal query handle
		void queryHandle(CQuery *query,void *handle);    ///< Sets internal query handle

      /// @brief Opens the database connection. 
      ///
      /// This method should be overwritten in derived classes
      /// @param connectionString std::string, the ODBC connection string
		virtual void openDatabase(std::string connectionString = "") throw(CException);

      /// @brief Closes the database connection.
      ///
      /// This method should be overwritten in derived classes
		virtual void closeDatabase() throw(CException);

      /// @brief Begins the transaction
      ///
      /// This method should be implemented in derived driver 
		virtual void driverBeginTransaction() throw(CException) { notImplemented("driverBeginTransaction"); }

      /// @brief Ends the transaction
      ///
      /// This method should be implemented in derived driver 
      /// @param commit bool, commit if true, rollback if false
		virtual void driverEndTransaction(bool commit) throw(CException) { notImplemented("driverEndTransaction"); }

      /// @brief Throws an exception
      ///
      /// Before exception is thrown, it is logged into the logfile (if the logfile is defined)
      /// @param method std::string, method name where error has occured
      /// @param error std::string, error text
		void logAndThrow(std::string method,std::string error) throw(CException);

	public:
      /// Destructor.
      /// Closes the database connection and all the connected queries.
      /// Releases all the database resources allocated during the connection.
		virtual ~CDatabase();

      /// Opens the database connection. If unsuccessful throws an exception.
      /// @param connectionString std::string, the ODBC connection string
		void open(std::string connectionString = "") throw(CException);

      /// @brief Closes the database connection. If unsuccessful throws an exception.
		void close() throw(CException);

      /// @brief Returns true if database is opened
		virtual bool active() const;

      /// @brief Returns the database connection handle
		virtual unsigned long handle() const;

      /// @brief Returns the connection string
		virtual std::string connectionString() const { return m_connString; }

      /// @brief Returns the driver description
		virtual std::string driverDescription() const { return m_driverDescription; }

      /// @brief Begins the transaction
		void beginTransaction() throw(CException);

      /// @brief Commits the transaction
		void commitTransaction() throw(CException);

      /// @brief Rolls back the transaction
		void rollbackTransaction()  throw(CException);

      /// @brief Reports true if in transaction
		int  inTransaction() { return m_inTransaction; }

      /// @brief Sets a log file for the database operations.
      /// 
      /// If the database log is set, the database would log the events in CDatabase and CQuery objects
      /// into this log. To stop the logging, set the logFile parameter to NULL, or deactivate the log.
      /// @param logFile CLogFile *, the log file object to use.
		void logFile(CFileLog *logFile) { m_log = logFile; }

      /// @brief Returns a log file for the database operations.
      /// @returns current log file ptr, ot NULL if log file isn't set
		CFileLog *logFile() const { return m_log; }

      /// @brief Adds the text to log file
      ///
      /// The log file should be set to active, otherwise no text is added to the log file.
      /// Every successful call of this method adds a new line to the log file.
      /// @param text std::string, log text
		void logText(std::string text) { if (m_log) m_log->writeString(text); }

      /// @brief Returns the datetime of last database operation
      /// 
      /// It may be useful if the connection to the database should be kept
      /// alive or if it should be dropped after some inactive time
		CDateTime lastActive() const { return m_lastActive; }
	};
   /// @}
}
#endif
