/* This file is part of the KDE project
   Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl>

   This program 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 program 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 program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "sqlitecursor.h"

#include "sqliteconnection.h"
#include "sqliteconnection_p.h"

#include "kexi_utils.h"

#include <kexidb/error.h>
#include <kexidb/driver.h>

#include <assert.h>
#include <string.h>
#include <stdlib.h>

#include <kdebug.h>
#include <klocale.h>

#include <qptrvector.h>
#include <qdatetime.h>

using namespace KexiDB;

//! safer interpretations of boolean values for SQLite
static bool sqliteStringToBool(const QString& s)
{
	return s.lower()=="yes" || (s.lower()!="no" && s!="0");
}

//----------------------------------------------------

class KexiDB::SQLiteCursorData : public SQLiteConnectionInternal
{
	public:
		SQLiteCursorData()
			:
			SQLiteConnectionInternal()
//			: curr_cols(0)
//			errmsg_p(0)
//			, res(SQLITE_OK)
			, curr_coldata(0)
			, curr_colname(0)
			, cols_pointers_mem_size(0)
//			, rec_stored(false)
/* MOVED TO Cursor:
			, cols_pointers_mem_size(0)
			, records_in_buf(0)
			, buffering_completed(false)
			, at_buffer(false)*/
//#ifdef SQLITE3
//			, rowDataReadyToFetch(false)
//#endif
		{
		}

/*#ifdef SQLITE3
		void fetchRowDataIfNeeded()
		{
			if (!rowDataReadyToFetch)
				return true;
			rowDataReadyToFetch = false;
			m_fieldCount = sqlite3_data_count(data);
			for (int i=0; i<m_fieldCount; i++) {
				
			}
		}
#endif*/

		QCString st;
		//for sqlite:
		sqlite_struct *data; //! taken from SQLiteConnection
#ifdef SQLITE2
		sqlite_vm *prepared_st_handle; //vm
#else //SQLITE3
		sqlite3_stmt *prepared_st_handle;
#endif
		
		char *utail;
		
//		QString errmsg; //<! server-specific message of last operation
//		char *errmsg_p; //<! temporary: server-specific message of last operation
//		int res; //<! result code of last operation on server

//		int curr_cols;
		const char **curr_coldata;
		const char **curr_colname;

		int next_cols;
//		const char **next_coldata;
//		const char **next_colname;
//		bool rec_stored : 1; //! true, current record is stored in next_coldata

/* MOVED TO Cursor:
		uint cols_pointers_mem_size; //! size of record's array of pointers to values
		int records_in_buf; //! number of records currently stored in the buffer
		bool buffering_completed; //! true if we have already all records stored in the buffer
		QPtrVector<const char*> records; //buffer data
		bool at_buffer; //! true if we already point to the buffer with curr_coldata
*/

/*		int prev_cols;
		const char **prev_coldata;
		const char **prev_colname;*/
		
		uint cols_pointers_mem_size; //! size of record's array of pointers to values
		QPtrVector<const char*> records;//! buffer data
//#ifdef SQLITE3
//		bool rowDataReadyToFetch : 1;
//#endif

#ifdef SQLITE3
	inline QVariant getValue(Field *f, int i)
	{
		int type = sqlite3_column_type(prepared_st_handle, i);
		if (type==SQLITE_NULL) {
			return QVariant();
		}
		else if (!f || type==SQLITE_TEXT) {
//TODO: support for UTF-16
#define GET_sqlite3_column_text QString::fromUtf8( (const char*)sqlite3_column_text(prepared_st_handle, i) )
			if (!f || f->isTextType())
				return GET_sqlite3_column_text;
			else {
				switch (f->type()) {
				case Field::Date:
					return QDate::fromString( GET_sqlite3_column_text, Qt::ISODate );
				case Field::Time:
					//QDateTime - a hack needed because QVariant(QTime) has broken isNull()
					return Kexi::stringToHackedQTime(GET_sqlite3_column_text);
				case Field::DateTime: {
					QString tmp( GET_sqlite3_column_text );
					tmp[10] = 'T'; //for ISODate compatibility
					return QDateTime::fromString( tmp, Qt::ISODate );
				}
				case Field::Boolean:
					return QVariant(sqliteStringToBool(GET_sqlite3_column_text), 1);
				default:
					return QVariant(); //TODO
				}
			}
		}
		else if (type==SQLITE_INTEGER) {
			switch (f->type()) {
			case Field::Byte:
			case Field::ShortInteger:
			case Field::Integer:
				return QVariant( sqlite3_column_int(prepared_st_handle, i) );
			case Field::BigInteger:
				return QVariant( (Q_LLONG)sqlite3_column_int64(prepared_st_handle, i) );
			case Field::Boolean:
				return QVariant( sqlite3_column_int(prepared_st_handle, i)!=0, 1 );
			default:;
			}
			if (f->isFPNumericType()) //WEIRD, YEAH?
				return QVariant( (double)sqlite3_column_int(prepared_st_handle, i) );
			else
				return QVariant(); //TODO
		}
		else if (type==SQLITE_FLOAT) {
			if (f->isFPNumericType())
				return QVariant( sqlite3_column_double(prepared_st_handle, i) );
			else if (f->isIntegerType())
				return QVariant( (double)sqlite3_column_double(prepared_st_handle, i) );
			else
				return QVariant(); //TODO
		}
		else if (type==SQLITE_BLOB) {
			if (f->type()==Field::BLOB) {
				QByteArray ba;
				ba.setRawData((const char*)sqlite3_column_blob(prepared_st_handle, i),
					sqlite3_column_bytes(prepared_st_handle, i));
				return ba;
			} else
				return QVariant(); //TODO
		}
		return QVariant();
	}
#endif //SQLITE3
};



SQLiteCursor::SQLiteCursor(Connection* conn, const QString& statement, uint options)
	: Cursor( conn, statement, options )
	, d( new SQLiteCursorData() )
{
	d->data = static_cast<SQLiteConnection*>(conn)->d->data;
}

SQLiteCursor::SQLiteCursor(Connection* conn, QuerySchema& query, uint options )
	: Cursor( conn, query, options )
	, d( new SQLiteCursorData() )
{
	d->data = static_cast<SQLiteConnection*>(conn)->d->data;
}

SQLiteCursor::~SQLiteCursor()
{
	close();
	delete d;
}

bool SQLiteCursor::drv_open(const QString& statement)
{
//	d->st.resize(statement.length()*2);
	//TODO: decode
//	d->st = statement.local8Bit();
//	d->st = m_conn->driver()->escapeString( statement.local8Bit() );

#ifdef SQLITE2
	d->st = statement.local8Bit();
	d->res = sqlite_compile(
		d->data,
		d->st.data(),
		(const char**)&d->utail,
		&d->prepared_st_handle,
		&d->errmsg_p );
	d->storeResult();
#else //SQLITE3
	d->st = statement.utf8();
	d->res = sqlite3_prepare(
		d->data,            /* Database handle */
		d->st.data(),       /* SQL statement, UTF-8 encoded */
		d->st.length(),             /* Length of zSql in bytes. */
		&d->prepared_st_handle,  /* OUT: Statement handle */
		0/*const char **pzTail*/     /* OUT: Pointer to unused portion of zSql */
	);
#endif
	if (d->res!=SQLITE_OK)
		return false;
//cursor is automatically @ first record
//	m_beforeFirst = true;

	if (isBuffered()) {
		d->records.resize(128); //TODO: manage size dynamically
	}

	return true;
}

/*bool SQLiteCursor::drv_getFirstRecord()
{
	bool ok = drv_getNextRecord();*/
/*	if ((m_options & Buffered) && ok) { //1st record is there:
		//compute parameters for cursor's buffer:
		//-size of record's array of pointer to values
		d->cols_pointers_mem_size = d->curr_cols * sizeof(char*);
		d->records_in_buf = 1;
	}*/
	/*return ok;
}*/

bool SQLiteCursor::drv_close()
{
#ifdef SQLITE2
	d->res = sqlite_finalize( d->prepared_st_handle, &d->errmsg_p );
#else //SQLITE3
	d->res = sqlite3_finalize( d->prepared_st_handle );
#endif
	d->storeResult();
	if (d->res!=SQLITE_OK) {
		return false;
	}
	return true;
}

void SQLiteCursor::drv_getNextRecord()
{
	static int _fieldCount;
#ifdef SQLITE2
	d->res = sqlite_step(
	 d->prepared_st_handle,
	 &_fieldCount,
	 &d->curr_coldata,
	 &d->curr_colname);
#else //SQLITE3
	d->res = sqlite3_step( d->prepared_st_handle );
#endif
	if (d->res == SQLITE_ROW) {
		m_result = FetchOK;
#ifdef SQLITE2
		m_fieldCount = (uint)_fieldCount;
#else
		m_fieldCount = sqlite3_data_count(d->prepared_st_handle);
//#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information
//      // -- just set a flag that we've a data not fetched but available
//		d->rowDataReadyToFetch = true;
#endif
	} else {
//#ifdef SQLITE3
//		d->rowDataReadyToFetch = false;
//#endif
		if (d->res==SQLITE_DONE)
			m_result = FetchEnd;
		else
			m_result = FetchError;
	}
	
	//debug
/*
	if (m_result == FetchOK && d->curr_coldata) {
		for (uint i=0;i<m_fieldCount;i++) {
			KexiDBDrvDbg<<"col."<< i<<": "<< d->curr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i]
			<< " = " << (d->curr_coldata[i] ? QString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)") <<endl;
		}
//		KexiDBDrvDbg << "SQLiteCursor::drv_getNextRecord(): "<<m_fieldCount<<" col(s) fetched"<<endl;
	}*/
}

void SQLiteCursor::drv_appendCurrentRecordToBuffer()
{
//	KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer():" <<endl;
	if (!d->cols_pointers_mem_size)
		d->cols_pointers_mem_size = m_fieldCount * sizeof(char*);
	const char **record = (const char**)malloc(d->cols_pointers_mem_size);
	const char **src_col = d->curr_coldata;
	const char **dest_col = record;
	for (uint i=0; i<m_fieldCount; i++,src_col++,dest_col++) {
//		KexiDBDrvDbg << i <<": '" << *src_col << "'" <<endl;
//		KexiDBDrvDbg << "src_col: " << src_col << endl;
		*dest_col = *src_col ? strdup(*src_col) : 0;
	}
	d->records.insert(m_records_in_buf,record);
//	KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer() ok." <<endl;
}

void SQLiteCursor::drv_bufferMovePointerNext()
{
	d->curr_coldata++; //move to next record in the buffer
}

void SQLiteCursor::drv_bufferMovePointerPrev()
{
	d->curr_coldata--; //move to prev record in the buffer
}

//compute a place in the buffer that contain next record's data
//and move internal buffer pointer to that place
void SQLiteCursor::drv_bufferMovePointerTo(Q_LLONG at)
{
	d->curr_coldata = d->records.at(at);
}

void SQLiteCursor::drv_clearBuffer()
{
	if (d->cols_pointers_mem_size>0) {
		const uint records_in_buf = m_records_in_buf;
		const char ***r_ptr = d->records.data();
		for (uint i=0; i<records_in_buf; i++, r_ptr++) {
	//		const char **record = m_records.at(i);
			const char **field_data = *r_ptr;
	//		for (int col=0; col<d->curr_cols; col++, field_data++) {
			for (uint col=0; col<m_fieldCount; col++, field_data++) {
				free((void*)*field_data); //free field memory
			}
			free(*r_ptr); //free pointers to fields array
		}
	}
//	d->curr_cols=0;
//	m_fieldCount=0;
	m_records_in_buf=0;
	d->cols_pointers_mem_size=0;
//	m_at_buffer=false;
	d->records.clear();
}

/*
void SQLiteCursor::drv_storeCurrentRecord()
{
#if 0
	assert(!m_data->rec_stored);
	m_data->rec_stored = true;
	m_data->next_cols = m_data->curr_cols;
	for (int i=0;i<m_data->curr_cols;i++) {
		KexiDBDrvDbg<<"[COPY] "<<i<<": "<< m_data->curr_coldata[i]<<endl;
		if (m_data->curr_coldata[i])
			m_data->next_coldata[i] = strdup( m_data->curr_coldata[i] );
		else
			m_data->next_coldata[i] = 0;
	}
#endif
}
*/

/*TODO
const char *** SQLiteCursor::bufferData()
{
	if (!isBuffered())
		return 0;
	return m_records.data();
}*/

const char ** SQLiteCursor::rowData() const
{
	return d->curr_coldata;
}

void SQLiteCursor::storeCurrentRow(RowData &data) const
{
#ifdef SQLITE2
	const char **col = d->curr_coldata;
#endif
	data.reserve(m_fieldCount);
	if (!m_fieldsExpanded) {//simple version: without types
		for( uint i=0; i<m_fieldCount; i++ ) {
#ifdef SQLITE2
			data[i] = QVariant( *col );
			col++;
#else //SQLITE3
			data[i] = QString::fromUtf8( (const char*)sqlite3_column_text(d->prepared_st_handle, i) );
#endif
		}
		return;
	}

	const uint fieldsExpandedCount = m_fieldsExpanded->count();
	for( uint i=0, j=0; i<m_fieldCount; i++, j++ ) {
//		while (j < m_detailedVisibility.count() && !m_detailedVisibility[j]) //!m_query->isColumnVisible(j))
//			j++;
		while (j < fieldsExpandedCount && !m_fieldsExpanded->at(j)->visible)
			j++;
		if (j >= fieldsExpandedCount) {
			//ERR!
			break;
		}
//		Field *f = m_fieldsExpanded->at(j);
		Field *f = m_fieldsExpanded->at(j)->field;
//		KexiDBDrvDbg << "SQLiteCursor::storeCurrentRow(): col=" << (col ? *col : 0) << endl;

#ifdef SQLITE2
		if (!*col)
			data[i] = QVariant();
		else if (f->isTextType())
# ifdef SQLITE_UTF8
			data[i] = QString::fromUtf8( *col );
# else
			data[i] = QVariant( *col ); //only latin1
# endif
		else if (f->isFPNumericType())
			data[i] = QVariant( QCString(*col).toDouble() );
		else {
			switch (f->type()) {
//todo: use short, etc.
			case Field::Byte:
			case Field::ShortInteger:
			case Field::Integer:
				data[i] = QVariant( QCString(*col).toInt() );
			case Field::BigInteger:
				data[i] = QVariant( QString::fromLatin1(*col).toLongLong() );
			case Field::Boolean:
				data[i] = QVariant( sqliteStringToBool(QString::fromLatin1(*col)), 1 );
				break;
			case Field::Date:
				data[i] = QDate::fromString( QString::fromLatin1(*col), Qt::ISODate );
				break;
			case Field::Time:
				//QDateTime - a hack needed because QVariant(QTime) has broken isNull()
				data[i] = Kexi::stringToHackedQTime(QString::fromLatin1(*col));
				break;
			case Field::DateTime: {
				QString tmp( QString::fromLatin1(*col) );
				tmp[10] = 'T';
				data[i] = QDateTime::fromString( tmp, Qt::ISODate );
				break;
			}
			default:
				data[i] = QVariant( *col );
			}
		}

		col++;
#else //SQLITE3
		data[i] = d->getValue(f, i);
/*		int type = sqlite3_column_type(d->prepared_st_handle, i);
		if (type==SQLITE_NULL) {
			data[i] = QVariant();
		}
		else if (type==SQLITE_INTEGER) {
			if (f->isIntegerType())
				data[i] = QVariant( sqlite3_column_int(d->prepared_st_handle, i) );
			else
				data[i] = QVariant(); //TODO
		}
		else if (type==SQLITE_FLOAT) {
			if (f->isFPNumericType())
				data[i] = QVariant( sqlite3_column_double(d->prepared_st_handle, i) );
			else
				data[i] = QVariant(); //TODO
		}
		else if (type==SQLITE_TEXT) {
//TODO: support for UTF-16
			if (f->isTextType())
				data[i] = QString::fromUtf8( (const char*)sqlite3_column_text(d->prepared_st_handle, i) );
			else
				data[i] = QVariant(); //TODO
		}
		else if (type==SQLITE_BLOB) {
			if (f->type()==KexiDB::Field::BLOB) {
				QByteArray ba;
				ba.setRawData((const char*)sqlite3_column_blob(d->prepared_st_handle, i),
					sqlite3_column_bytes(d->prepared_st_handle, i));
				data[i] = ba;
			} else
				data[i] = QVariant(); //TODO
		}*/
#endif
	}
}

QVariant SQLiteCursor::value(uint i)
{
	if (i > (m_fieldCount-1)) //range checking
		return QVariant();
//TODO: allow disable range checking! - performance reasons
//	const KexiDB::Field *f = m_query ? m_query->field(i) : 0;
	KexiDB::Field *f = (m_fieldsExpanded && i<m_fieldsExpanded->count())
		? m_fieldsExpanded->at(i)->field : 0;
#ifdef SQLITE2
	//from most to least frequently used types:
	if (!f || f->isTextType())
		return QVariant( d->curr_coldata[i] );
	else if (f->isIntegerType())
		return QVariant( QCString(d->curr_coldata[i]).toInt() );
	else if (f->isFPNumericType())
		return QVariant( QCString(d->curr_coldata[i]).toDouble() );

	return QVariant( d->curr_coldata[i] ); //default
#else
	return d->getValue(f, i);
#endif
}

/*! Stores string value taken from field number \a i to \a str.
 \return false when range checking failed. 
bool SQLiteCursor::storeStringValue(uint i, QString &str)
{
	if (i > (m_fieldCount-1)) //range checking
		return false;
	str = d->curr_coldata[i];
	return true;
}*/

int SQLiteCursor::serverResult()
{
	return d->res;
}

QString SQLiteCursor::serverResultName()
{
#ifdef SQLITE2
	return QString::fromLatin1( sqlite_error_string(d->res) );
#else //SQLITE3
	return QString::fromLatin1( d->result_name );
#endif
}

QString SQLiteCursor::serverErrorMsg()
{
	return d->errmsg;
}

void SQLiteCursor::drv_clearServerResult()
{
	d->res = SQLITE_OK;
	d->errmsg_p = 0;
}

