using System;
using System.Diagnostics;
using System.Text;

namespace Community.CsharpSqlite
{
  using sqlite3_callback = Sqlite3.dxCallback;
  using sqlite3_stmt = Sqlite3.Vdbe;

  public partial class Sqlite3
  {
    /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** Main file for the SQLite library.  The routines in this file
    ** implement the programmer interface to the library.  Routines in
    ** other files are for internal use by SQLite and should not be
    ** accessed by users of the library.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
    **
    *************************************************************************
    */

    //#include "sqliteInt.h"

    /*
    ** Execute SQL code.  Return one of the SQLITE_ success/failure
    ** codes.  Also write an error message into memory obtained from
    ** malloc() and make pzErrMsg point to that message.
    **
    ** If the SQL is a query, then for each row in the query result
    ** the xCallback() function is called.  pArg becomes the first
    ** argument to xCallback().  If xCallback=NULL then no callback
    ** is invoked, even for queries.
    */
    //C# Alias
    static public int exec( sqlite3 db,             /* The database on which the SQL executes */    string zSql,            /* The SQL to be executed */    int NoCallback, int NoArgs, int NoErrors )
    {
      string Errors = "";
      return sqlite3_exec( db, zSql, null, null, ref Errors );
    }

    static public int exec( sqlite3 db,             /* The database on which the SQL executes */    string zSql,                /* The SQL to be executed */    sqlite3_callback xCallback, /* Invoke this callback routine */    object pArg,                /* First argument to xCallback() */    int NoErrors )
    {
      string Errors = "";
      return sqlite3_exec( db, zSql, xCallback, pArg, ref Errors );
    }
    static public int exec( sqlite3 db,             /* The database on which the SQL executes */    string zSql,                /* The SQL to be executed */    sqlite3_callback xCallback, /* Invoke this callback routine */    object pArg,                /* First argument to xCallback() */    ref string pzErrMsg         /* Write error messages here */)
    {
      return sqlite3_exec( db, zSql, xCallback, pArg, ref pzErrMsg );
    }

    //OVERLOADS 
    static public int sqlite3_exec(
    sqlite3 db,             /* The database on which the SQL executes */
    string zSql,            /* The SQL to be executed */
    int NoCallback, int NoArgs, int NoErrors
    )
    {
      string Errors = "";
      return sqlite3_exec( db, zSql, null, null, ref Errors );
    }

    static public int sqlite3_exec(
    sqlite3 db,             /* The database on which the SQL executes */
    string zSql,                /* The SQL to be executed */
    sqlite3_callback xCallback, /* Invoke this callback routine */
    object pArg,                /* First argument to xCallback() */
    int NoErrors
    )
    {
      string Errors = "";
      return sqlite3_exec( db, zSql, xCallback, pArg, ref Errors );
    }
    static public int sqlite3_exec(
    sqlite3 db,             /* The database on which the SQL executes */
    string zSql,                /* The SQL to be executed */
    sqlite3_callback xCallback, /* Invoke this callback routine */
    object pArg,                /* First argument to xCallback() */
    ref string pzErrMsg         /* Write error messages here */
    )
    {

      int rc = SQLITE_OK;         /* Return code */
      string zLeftover = "";      /* Tail of unprocessed SQL */
      sqlite3_stmt pStmt = null;  /* The current SQL statement */
      string[] azCols = null;     /* Names of result columns */
      int nRetry = 0;             /* Number of retry attempts */
      int callbackIsInit;         /* True if callback data is initialized */

      if ( !sqlite3SafetyCheckOk( db ) )
        return SQLITE_MISUSE_BKPT();

      if ( zSql == null )
        zSql = "";

      sqlite3_mutex_enter( db.mutex );
      sqlite3Error( db, SQLITE_OK, 0 );
      while ( ( rc == SQLITE_OK || ( rc == SQLITE_SCHEMA && ( ++nRetry ) < 2 ) ) && zSql != "" )
      {
        int nCol;
        string[] azVals = null;

        pStmt = null;
        rc = sqlite3_prepare( db, zSql, -1, ref pStmt, ref zLeftover );
        Debug.Assert( rc == SQLITE_OK || pStmt == null );
        if ( rc != SQLITE_OK )
        {
          continue;
        }
        if ( pStmt == null )
        {
          /* this happens for a comment or white-space */
          zSql = zLeftover;
          continue;
        }

        callbackIsInit = 0;
        nCol = sqlite3_column_count( pStmt );

        while ( true )
        {
          int i;
          rc = sqlite3_step( pStmt );

          /* Invoke the callback function if required */
          if ( xCallback != null && ( SQLITE_ROW == rc ||
          ( SQLITE_DONE == rc && callbackIsInit == 0
          && ( db.flags & SQLITE_NullCallback ) != 0 ) ) )
          {
            if ( 0 == callbackIsInit )
            {
              azCols = new string[nCol];//sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
              //if ( azCols == null )
              //{
              //  goto exec_out;
              //}
              for ( i = 0; i < nCol; i++ )
              {
                azCols[i] = sqlite3_column_name( pStmt, i );
                /* sqlite3VdbeSetColName() installs column names as UTF8
                ** strings so there is no way for sqlite3_column_name() to fail. */
                Debug.Assert( azCols[i] != null );
              }
              callbackIsInit = 1;
            }
            if ( rc == SQLITE_ROW )
            {
              azVals = new string[nCol];// azCols[nCol];
              for ( i = 0; i < nCol; i++ )
              {
                azVals[i] = sqlite3_column_text( pStmt, i );
                if ( azVals[i] == null && sqlite3_column_type( pStmt, i ) != SQLITE_NULL )
                {
                  //db.mallocFailed = 1;
                  //goto exec_out;
                }
              }
            }
            if ( xCallback( pArg, nCol, azVals, azCols ) != 0 )
            {
              rc = SQLITE_ABORT;
              sqlite3VdbeFinalize( ref pStmt );
              pStmt = null;
              sqlite3Error( db, SQLITE_ABORT, 0 );
              goto exec_out;
            }
          }

          if ( rc != SQLITE_ROW )
          {
            rc = sqlite3VdbeFinalize( ref pStmt );
            pStmt = null;
            if ( rc != SQLITE_SCHEMA )
            {
              nRetry = 0;
              if ( ( zSql = zLeftover ) != "" )
              {
                int zindex = 0;
                while ( zindex < zSql.Length && sqlite3Isspace( zSql[zindex] ) )
                  zindex++;
                if ( zindex != 0 )
                  zSql = zindex < zSql.Length ? zSql.Substring( zindex ) : "";
              }
            }
            break;
          }
        }

        sqlite3DbFree( db, ref azCols );
        azCols = null;
      }

exec_out:
      if ( pStmt != null )
        sqlite3VdbeFinalize( ref pStmt );
      sqlite3DbFree( db, ref azCols );

      rc = sqlite3ApiExit( db, rc );
      if ( rc != SQLITE_OK && ALWAYS( rc == sqlite3_errcode( db ) ) && pzErrMsg != null )
      {
        //int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db));
        //pzErrMsg = sqlite3Malloc(nErrMsg);
        //if (pzErrMsg)
        //{
        //   memcpy(pzErrMsg, sqlite3_errmsg(db), nErrMsg);
        //}else{
        //rc = SQLITE_NOMEM;
        //sqlite3Error(db, SQLITE_NOMEM, 0);
        //}
        pzErrMsg = sqlite3_errmsg( db );
      }
      else if ( pzErrMsg != "" )
      {
        pzErrMsg = "";
      }

      Debug.Assert( ( rc & db.errMask ) == rc );
      sqlite3_mutex_leave( db.mutex );
      return rc;
    }
  }
}
