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

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

#include <vector>

/// @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
   CLogFile *    m_log;            ///< Log for the database events (optional)
   std::string   m_objectName;     ///< Object name for logs and error messages

   /// 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(CLogFile *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
   CLogFile *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); }
};

/// @}

#endif
