// (c) 2004 Mark Kretschmann <markey@web.de>
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2004 Sami Nieminen <sami.nieminen@iki.fi>
// (c) 2005 Ian Monroe <ian@monroe.nu>
// See COPYING file for licensing information.

#ifndef KLAMAV_COLLECTIONDB_H
#define KLAMAV_COLLECTIONDB_H

#include <kurl.h>
#include <tqdir.h>            //stack allocated
#include <tqimage.h>
#include <tqobject.h>         //baseclass
#include <tqptrqueue.h>       //baseclass
#include <tqsemaphore.h>      //stack allocated
#include <tqstringlist.h>     //stack allocated

class DbConnection;
class DbConnectionPool;


class DbConfig
{};


class SqliteConfig : public DbConfig
{
    public:
        SqliteConfig( const TQString& /* dbfile */ );

        const TQString dbFile() const { return m_dbfile; }

    private:
        TQString m_dbfile;
};



class DbConnection
{
    public:
        enum DbConnectionType { sqlite = 0, mysql = 1, postgresql = 2 };

        DbConnection( DbConfig* /* config */ );
        virtual ~DbConnection() = 0;

        virtual TQStringList query( const TQString& /* statement */ ) = 0;
        virtual int insert( const TQString& /* statement */, const TQString& /* table */ ) = 0;
        const bool isInitialized() const { return m_initialized; }
        virtual bool isConnected() const = 0;
        virtual const TQString lastError() const { return "None"; }
    protected:
        bool m_initialized;
        DbConfig *m_config;
};


typedef struct sqlite3 sqlite3;
typedef struct sqlite3_context sqlite3_context;
typedef struct Mem sqlite3_value;

class SqliteConnection : public DbConnection
{
    public:
        SqliteConnection( SqliteConfig* /* config */ );
       ~SqliteConnection();

        TQStringList query( const TQString& /* statement */ );
        int insert( const TQString& /* statement */, const TQString& /* table */ );
        bool isConnected()const { return true; }
    private:
        static void sqlite_rand(sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/);
        static void sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv);

        sqlite3* m_db;
};




class DbConnectionPool : TQPtrQueue<DbConnection>
{
    public:
        DbConnectionPool( bool temporary );
       ~DbConnectionPool();

        const DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; }
        const DbConfig *getDbConfig() const { return m_dbConfig; }
        void createDbConnections();

        DbConnection *getDbConnection();
        void putDbConnection( const DbConnection* /* conn */ );

        TQString escapeString( TQString string )
        {
            return
                    string.replace( '\'', "''" );
        }

    private:
        static const int POOL_SIZE = 5;

        bool m_isTemporary;
        TQSemaphore m_semaphore;
        DbConnection::DbConnectionType m_dbConnType;
        DbConfig *m_dbConfig;
};


class CollectionDB : public TQObject
{
    Q_OBJECT


    signals:

    public:
        CollectionDB( bool temporary = false );
        ~CollectionDB();

        static CollectionDB *instance();

        const TQString escapeString( const TQString &string ) { return m_dbConnPool->escapeString(string); }
        const TQString boolT() { if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) return "'t'"; else return "1"; }
        const TQString boolF() { if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) return "'f'"; else return "0"; }
        const TQString textColumnType() { if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return "VARCHAR(255)"; }
        const TQString textColumnType(int length){ if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return TQString("VARCHAR(%1)").arg(length); }
        // We might consider using LONGTEXT type, as some lyrics could be VERY long..???
        const TQString longTextColumnType() { if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return "TEXT"; }
        const TQString randomFunc() { if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) return "random()"; else return "RAND()"; }

        int getType() { return m_dbConnPool->getDbConnectionType(); }


        /**
         * This method returns a static DbConnection for components that want to use
         * the same connection for the whole time. Should not be used anywhere else
         * but in CollectionReader.
         *
         * @return static DbConnection
         */
        DbConnection *getStaticDbConnection();

        /**
         * Returns the DbConnection back to connection pool.
         *
         * @param conn DbConnection to be returned
         */
        void returnStaticDbConnection( DbConnection *conn );

        //sql helper methods
        TQStringList query( const TQString& statement, DbConnection *conn = NULL );
        int insert( const TQString& statement, const TQString& table, DbConnection *conn = NULL );

        //table management methods
        bool isEmpty();
        bool isValid(const TQString &column, const TQString &table);
        void createTables( DbConnection *conn = NULL );
        void createActivityTable( DbConnection *conn = NULL );
        void createMetaDBTable( DbConnection *conn = NULL );
        void loadMetaDBTable( DbConnection *conn = NULL );
        void dropTables( DbConnection *conn = NULL );
        void clearTables( DbConnection *conn = NULL );
        void moveTempTables( DbConnection *conn );


	TQString typeCount( const TQString &type_id );
	TQStringList messagesForType( const TQString &type_id,  const bool isValue );
	TQStringList allActivity( );
	TQStringList allActivityOfType(const  TQString &type, const  TQString &days );
	void insertEvent(const  TQString &type, const TQString &event,const TQString &file = NULL, DbConnection *conn = NULL );
	void expireActivity(const  TQString &days );
    void insertMetaDBEntry(const  TQString &date, const TQString &submission, const TQString &creator,const TQString &virus,const TQString &alias, const TQString &sender,DbConnection *conn = NULL);
    TQString latestMetaDBDate( );

    protected:
        TQCString md5sum( const TQString& artist, const TQString& album, const TQString& file = TQString::null );
        /** Manages regular folder monitoring scan */

    public slots:

    private slots:

    private:
        //bump DATABASE_VERSION whenever changes to the table structure are made. will remove old db file.
        static const int DATABASE_VERSION = 20;
        static const int DATABASE_STATS_VERSION = 3;
        static const int MONITOR_INTERVAL = 60; //sec
        static const bool DEBUGSQL = false;

        void initialize();
        void destroy();

        //general management methods


        uint IDFromValue( TQString name, TQString value, bool autocreate = true, const bool temporary = false,
                          const bool updateSpelling = false, DbConnection *conn = NULL );

        TQString valueFromID( TQString table, uint id );

        //member variables

        DbConnectionPool *m_dbConnPool;

        bool m_isTemporary;
        bool m_monitor;
};




#endif /* KLAMAV_COLLECTIONDB_H */