/*
    statisticsdb.cpp

    Copyright (c) 2003-2004 by Marc Cramdal        <marc.cramdal@gmail.com>


    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <tqfile.h>

#include "sqlite3.h"

#include <kgenericfactory.h>
#include <tdeaboutdata.h>
#include <tdeaction.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <tdeversion.h>

#include "statisticsdb.h"

#include <unistd.h>
#include <time.h>

StatisticsDB::StatisticsDB()
{
	TQCString path = (::locateLocal("appdata", "kopete_statistics-0.1.db")).latin1();
	kdDebug() << "statistics: DB path:" << path << endl;

	// Open database file and check for correctness
	bool failOpen = true;
	TQFile file( path );
	if ( file.open( IO_ReadOnly ) ) 
	{
		TQString format;
		file.readLine( format, 50 );
		if ( !format.startsWith( "SQLite format 3" ) ) 
		{
			kdWarning() << "[statistics] Database versions incompatible. Removing and rebuilding database.\n";
		}
		else if ( sqlite3_open( path, &m_db ) != SQLITE_OK ) 
		{
			kdWarning() << "[statistics] Database file corrupt. Removing and rebuilding database.\n";
			sqlite3_close( m_db );
		}
		else
			failOpen = false;
	}
	
	if ( failOpen ) 
	{
	// Remove old db file; create new
	TQFile::remove( path );
	sqlite3_open( path, &m_db );
	}

	kdDebug() << "[Statistics] Contructor"<< endl;

	// Creates the tables if they do not exist.
	TQStringList result = query("SELECT name FROM sqlite_master WHERE type='table'");
	
	if (!result.contains("contacts"))
	{
		query(TQString("CREATE TABLE contacts "
			"(id INTEGER PRIMARY KEY,"
			"statisticid TEXT,"
			"contactid TEXT"
			");"));
	}

	if (!result.contains("contactstatus"))
	{
		kdDebug() << "[Statistics] Database empty"<< endl;
		query(TQString("CREATE TABLE contactstatus "
			"(id INTEGER PRIMARY KEY,"
			"metacontactid TEXT,"
			"status TEXT,"
			"datetimebegin INTEGER,"
			"datetimeend INTEGER"
			");"));
	}
	
	if (!result.contains("commonstats"))
	{
		// To store things like the contact answer time etc.
		query(TQString("CREATE TABLE commonstats"
			" (id INTEGER PRIMARY KEY,"
			"metacontactid TEXT,"
			"statname TEXT," // for instance, answertime, lastmessage, messagelength ...
			"statvalue1 TEXT,"
			"statvalue2 TEXT"
			");"));
	}

	/// @fixme This is not used anywhere
	if (!result.contains("statsgroup"))
	{
		query(TQString("CREATE TABLE statsgroup"
			"(id INTEGER PRIMARY KEY,"
			"datetimebegin INTEGER,"
			"datetimeend INTEGER,"
			"caption TEXT);"));
	}
 
}

StatisticsDB::~StatisticsDB()
{
	sqlite3_close(m_db);
} 

 /**
  * Executes a SQL query on the already opened database
  * @param statement SQL program to execute. Only one SQL statement is allowed.
  * @param debug     Set to true for verbose debug output.
  * @retval names    Will contain all column names, set to NULL if not used.
  * @return          The queried data, or TQStringList() on error.
  */
 TQStringList StatisticsDB::query( const TQString& statement, TQStringList* const names, bool debug )
 {
 
     if ( debug )
         kdDebug() << "query-start: " << statement << endl;
 
     clock_t start = clock();
 
     if ( !m_db )
     {
         kdError() << k_funcinfo << "[CollectionDB] SQLite pointer == NULL.\n";
         return TQStringList();
     }
 
     int error;
     TQStringList values;
     const char* tail;
     sqlite3_stmt* stmt;
 
     //compile SQL program to virtual machine
     error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail );
 
     if ( error != SQLITE_OK )
     {
         kdError() << k_funcinfo << "[CollectionDB] sqlite3_compile error:" << endl;
         kdError() << sqlite3_errmsg( m_db ) << endl;
         kdError() << "on query: " << statement << endl;
 
         return TQStringList();
     }
 
     int busyCnt = 0;
     int number = sqlite3_column_count( stmt );
     //execute virtual machine by iterating over rows
     while ( true )
     {
         error = sqlite3_step( stmt );
 
         if ( error == SQLITE_BUSY )
         {
             if ( busyCnt++ > 20 ) {
                 kdError() << "[CollectionDB] Busy-counter has reached maximum. Aborting this sql statement!\n";
                 break;
             }
             ::usleep( 100000 ); // Sleep 100 msec
             kdDebug() << "[CollectionDB] sqlite3_step: BUSY counter: " << busyCnt << endl;
         }
         if ( error == SQLITE_MISUSE )
             kdDebug() << "[CollectionDB] sqlite3_step: MISUSE" << endl;
         if ( error == SQLITE_DONE || error == SQLITE_ERROR )
             break;
 
         //iterate over columns
         for ( int i = 0; i < number; i++ )
         {
             values << TQString::fromUtf8( (const char*) sqlite3_column_text( stmt, i ) );
             if ( names ) *names << TQString( sqlite3_column_name( stmt, i ) );
         }
     }
     //deallocate vm ressources
     sqlite3_finalize( stmt );
 
     if ( error != SQLITE_DONE )
     {
         kdError() << k_funcinfo << "sqlite_step error.\n";
         kdError() << sqlite3_errmsg( m_db ) << endl;
         kdError() << "on query: " << statement << endl;
 
         return TQStringList();
     }
 
     if ( debug )
     {
         clock_t finish = clock();
         const double duration = (double) (finish - start) / CLOCKS_PER_SEC;
         kdDebug() << "[CollectionDB] SQL-query (" << duration << "s): " << statement << endl;
     }
 
 
    return values;
}
