/*******************************************************************************
*                         Goggles Music Manager                                *
********************************************************************************
*           Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved      *
*                               ---                                            *
* 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 3 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 General Public License for more details.                                 *
*                                                                              *
* You should have received a copy of the GNU General Public License            *
* along with this program.  If not, see http://www.gnu.org/licenses.           *
********************************************************************************/
#include "gmdefs.h"

// Index out of range
const FXchar FXExecuteException::exceptionName[]="Query Execute Failed";
const FXchar FXCompileException::exceptionName[]="Query Compile Failed";
const FXchar FXQueryException::exceptionName[]="Query Exception";

GMQuery::GMQuery() : statement(NULL) {
  }

GMQuery::GMQuery(GMDatabase * database,const FXString & q) : statement(NULL) {
  compile(database,q);
  }

void GMQuery::compile(GMDatabase * database,const FXString & q) {
  if (statement) {
    sqlite3_finalize((sqlite3_stmt*)statement);
    statement=NULL;
    }

#if SQLITE_VERSION_NUMBER > 3003011
  FXint result = sqlite3_prepare_v2(database->db,q.text(),-1,&statement,NULL);
#else
  FXint result = sqlite3_prepare(database->db,q.text(),-1,&statement,NULL);
#endif
  if (result!=SQLITE_OK){
#ifdef DEBUG
    fxmessage("GMQuery::compile failed (%d): %s\n",result,q.text());
    fxmessage("Reason: %s\n",sqlite3_errmsg(database->db));
#endif
    throw FXCompileException();
    }
  }


void GMQuery::compile(GMDatabase & database,const FXString & q) {
  if (statement) {
    sqlite3_finalize((sqlite3_stmt*)statement);
    statement=NULL;
    }

#if SQLITE_VERSION_NUMBER > 3003011
  FXint result = sqlite3_prepare_v2(database.db,q.text(),-1,&statement,NULL);
#else
  FXint result = sqlite3_prepare(database.db,q.text(),-1,&statement,NULL);
#endif
  if (result!=SQLITE_OK){
#ifdef DEBUG
    fxmessage("GMQuery::compile failed (%d): %s\n",result,q.text());
    fxmessage("Reason: %s\n",sqlite3_errmsg(database.db));
#endif
    throw FXCompileException();
    }
  }

FXbool GMQuery::execute() {
  if (statement) {
    FXint result = sqlite3_step(statement);
    switch(result) {
      case SQLITE_DONE: return false; break;
      case SQLITE_ROW : return true;  break;
      default					: reset(); throw FXExecuteException(); break;
      }
    return false;
    }
  return false;
  }

FXbool GMQuery::perform() {
  if (statement) {
    FXint result;
    while(1) {
      result = sqlite3_step(statement);
      switch(result) {
        case SQLITE_DONE: return true; break;
        case SQLITE_ROW : continue;  break;
        default					: reset(); throw FXExecuteException(); break;
        }
      }
    }
  return true;
  }


void GMQuery::clear(){
  if (statement) {
    sqlite3_finalize((sqlite3_stmt*)statement);
    statement=NULL;
    }
  }


/// Return number of columns the query returns
FXint GMQuery::getNumFields() const {
  if (statement)
    return sqlite3_column_count((sqlite3_stmt*)statement);
  else
    return 0;
  }

FXint GMQuery::getNumData() const {
  if (statement)
    return sqlite3_data_count((sqlite3_stmt*)statement);
  else
    return 0;
  }


/// Return the specified column name the query returned.
FXString GMQuery::getFieldName(FXint i){
  if (statement)
    return sqlite3_column_name((sqlite3_stmt*)statement,i);
  else
    return FXString::null;
  }

/// Return the column type for the specified column
FXFieldType GMQuery::getFieldType(FXint i){
  const FXchar * type=NULL;
  if (statement) {
    type = sqlite3_column_decltype((sqlite3_stmt*)statement,i);
    if (type==NULL)
      return TYPE_NULL;
    else if (comparecase("INTEGER",type)==0)
      return TYPE_INTEGER;
    else if (comparecase("FLOAT",type)==0)
      return TYPE_FLOAT;
    else if (comparecase("TEXT",type)==0)
      return TYPE_TEXT;
    else if (comparecase("BLOB",type)==0)
      return TYPE_BLOB;
    else
      return TYPE_NULL;
    }
  else {
    return TYPE_NULL;
    }
  }



void GMQuery::setParameter(FXint p,FXint v){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_int((sqlite3_stmt*)statement,(p+1),v);
  }

void GMQuery::setParameter(FXint p,FXuint v){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_int((sqlite3_stmt*)statement,(p+1),(FXint)v);
  }

void GMQuery::setParameter(FXint p,FXlong v){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_int64((sqlite3_stmt*)statement,(p+1),v);
  }

void GMQuery::setParameter(FXint p,FXdouble v){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_double((sqlite3_stmt*)statement,(p+1),v);
  }

void GMQuery::setParameter(FXint p,FXfloat v){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_double((sqlite3_stmt*)statement,(p+1),v);
  }

void GMQuery::setParameter(FXint p,const FXString & text){
  if (!statement) fxerror("GMQuery::setParameter called without compiled query\n");
  sqlite3_bind_text((sqlite3_stmt*)statement,(p+1),text.text(),-1,SQLITE_TRANSIENT);
  }

FXint GMQuery::getNumParameters() {
  if (!statement) fxerror("GMQuery::getNumParameters called without compiled query\n");
  return sqlite3_bind_parameter_count((sqlite3_stmt*)statement);
  }

FXint GMQuery::getParameterIndex(const FXString & name){
  if (!statement) fxerror("GMQuery::getParameterIndex called without compiled query\n");
  FXint index = sqlite3_bind_parameter_index((sqlite3_stmt*)statement,name.text());
  if (index==0) return -1;
  return (index-1);
  }

FXString GMQuery::getParameterName(FXint p){
  if (!statement) fxerror("GMQuery::getParameterName called without compiled query\n");

  const FXchar * name  = sqlite3_bind_parameter_name((sqlite3_stmt*)statement,p+1);
  if (name==NULL)
    return "?";
  else
    return name;
  }


void GMQuery::reset() {
  if (statement) {
    sqlite3_reset(statement);
    }
  }

void GMQuery::getResult(FXint column,FXString & v){
  if (getNumData())
    v = (const FXchar *) sqlite3_column_text((sqlite3_stmt*)statement,column);
  }

void GMQuery::getResult(FXint column,FXint & v){
  if (getNumData())
    v = sqlite3_column_int((sqlite3_stmt*)statement,column);
  }

void GMQuery::getResult(FXint column,FXuint & v){
  if (getNumData())
    v = (FXuint) sqlite3_column_int((sqlite3_stmt*)statement,column);
  }

void GMQuery::getResult(FXint column,FXdouble & v){
 if (getNumData() && sqlite3_column_type((sqlite3_stmt*)statement,column)==SQLITE_FLOAT){
    v=sqlite3_column_double((sqlite3_stmt*)statement,column);
    }
  else {
    v=NAN;
    }
  }

void GMQuery::getResult(FXint column,FXfloat & v){
 if (getNumData() && sqlite3_column_type((sqlite3_stmt*)statement,column)==SQLITE_FLOAT){
    v=sqlite3_column_double((sqlite3_stmt*)statement,column);
    }
  else {
    v=NAN;
    }
  }


void GMQuery::getResult(FXint column,FXlong & v){
 if (getNumData())
    v = sqlite3_column_int64((sqlite3_stmt*)statement,column);
  }


const FXchar * GMQuery::getResult(FXint column){
  if (!getNumData()) return NULL;
  return (const FXchar *) sqlite3_column_text((sqlite3_stmt*)statement,column);
  }

void GMQuery::getResult(FXint column,FXbool & v){
 if (getNumData()) {
    FXint i;
    i = sqlite3_column_int((sqlite3_stmt*)statement,column);
    v =  (i==0) ? FALSE : TRUE;
    }
  }

GMQuery::~GMQuery(){
  clear();
  }


/*
void fillTable(GMQuery * query,FXTable * table){
  FXint 	integer;
  FXfloat real;
  FXString text;
  FXint rows=0;
  FXint ncols=0;
  table.clearItems();

  if (query.execute()) {
    ncols=query->getNumFields();
    table->setTableSize(1,ncols);
    for (FXint i=0;i<ncols;i++){
      table->setColumnText(i,query->getFieldName());
      switch(query->getFieldType()){
        case TYPE_INTEGER :
          query->getResult(i,integer);
          table->setItemTex(rows,i,FXStringVal(integer));
          break;
        case TYPE_FLOAT :
          query->getResult(i,real);
          table->setItemText(rows,i,FXStringVal(real));
          break;
        case TYPE_TEXT	:
          query->getResult(i,text);
          table->setItemText(rows,i,text);
          break;
        }
      }
    rows++;
    }
  while(query.execute()){
    table->insertRows(rows,1);
    for (FXint i=0;i<ncols;i++){
      table->setColumnText(i,query->getFieldName());
      switch(query->getFieldType()){
        case TYPE_INTEGER :
          query->getResult(i,integer);
          table->setItemTex(rows,i,FXStringVal(integer));
          break;
        case TYPE_FLOAT :
          query->getResult(i,real);
          table->setItemText(rows,i,FXStringVal(real));
          break;
        case TYPE_TEXT	:
          query->getResult(i,text);
          table->setItemText(rows,i,text);
          break;
        }
      }
    rows++;
    }
  }
*/





