/*! \file main.c
*	\brief all functions of this API
*   \author Stephane Bisaro
*/
/*

This source file is part of Scol
For the latest info, see http://www.scolring.org

Copyright (c) 2011 Stephane Bisaro, aka Iri <iri@irizone.net>

This source is under the terms of the Scol License. See the COPYING included file
for more informations.

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.

For others informations, please contact us from http://www.scolring.org/

*/

/* Warning !

SERVER SCOL LINUX VERSION ! 

*/

#ifdef __cplusplus
#error This source file is not C++ but rather C. Please use a C-compiler
#endif

#include "main.h"



cbmachine ww;





/**
 * \brief Internal Don't use. - Create a generic database connection
 *
 * Warning : the filename (if filename) must be encoded in utf-8 format (windows system only)
 */
int sqliteOpen (mmachine m, char* f, int flag)
{
    sqlite3    *db;
    int        l, objtab, res;
    int k = 0;

    MMechostr (MSKDEBUG, "sqliteOpen : entering ...\n");
    /* MMechostr(MSKDEBUG, "\n_SQLITE initializing and opening a database \" %s \"... \n", &f);*/
    /* db = NULL;*/
    res = sqlite3_open_v2 (f, &db, (int) flag, NULL);

    if (res != SQLITE_OK)
    {
            sqlite3_close (db);
            db = NULL;
            return res;
    }

    /* memory definition*/
    l = (sizeof (db) + 3) >> 2;
	objtab = MMmalloc(m, l, TYPETAB);

	if (objtab == NIL)
	{
	    SAFEdelete (db);
	    MMpull (m);
	    return -1;
	}

    MMstore ( m, objtab, 0, (int) db );
    MMpush (m, PTOM (objtab));
    k = OBJcreate (m, ObjSqliteType, (int) db, -1, -1);
    return res;
}

/**
 * \brief Scol function _sqliteOpenFileEx : fun [Chn P I I I] ObjSqlite
 *
 * Open a new connection (file)
 * Ouvre une connection sur base fichier / Connect a file database
 *
 * \param Chn : a channel
 * \param P : a read-reference file
 * \param I : flag : SQLITE_READWRITE (default) or SQLITE_ONLYREAD
 * \param I : flag : SQLITE_OPEN_PRIVATE_CACHE (default) or SQLITE_OPEN_SHAREDCACHE (see _sqliteSharedCacheEnabled too)
 * \param I : flag : SQLITE_OPEN_FULLMUTEX (default) or SQLITE_OPEN_NOMUTEX (multi-thread)
 * \return ObjSqlite : a new object or nil if error
 */
int _sqliteOpenFileEx (mmachine m)
{
    int mchannel, mfilename, mflagrw, mflagcache, mflagmutex;
    char* name;
    int res;
    int flag = 0;
    int flagrw = SQLITE_OPEN_READWRITE;
    int flagcache = SQLITE_OPEN_PRIVATECACHE;
    int flagmutex = SQLITE_OPEN_FULLMUTEX;

    MMechostr (MSKDEBUG, "_sqliteOpenFileEx : entering ...\n");

    mflagmutex = MTOI (MMpull (m));
    mflagcache = MTOI (MMpull (m));
    mflagrw = MTOI (MMpull (m));
    mfilename = MMpull (m);
    mchannel = MMget (m, 0);

    if ((mchannel == NIL) || (mfilename == NIL))
    {
        MMechostr(0, "_sqliteOpenFileEx error : channel or filename is nil\n");
        MMpull (m); /*MMpull (m); MMpull (m);*/
        MMpush (m, NIL);
        return 0;
    }

    if (mflagrw == SCOL_SQLITE_ONLYREAD) flagrw = SQLITE_OPEN_READONLY;
    if (mflagcache == SCOL_SQLITE_OPEN_SHAREDCACHE) flagcache = SQLITE_OPEN_SHAREDCACHE;
    flag += flagrw;
    flag += flagcache;
    flag += flagmutex;

    name = (char*) MMstartstr (m, MTOP (mfilename));

    if ((strchr (name, ':')) == 0)         /* reserved keywords in SQLite3 and greater*/
    {
        MMechostr (0, "_sqliteOpenFileEx error :  the filename content a forbidden character (\":\") ... \n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    res = sqliteOpen (m, name, flag);

    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteOpenFileEx error :  can not open the database. Error code is : %d ...\n", res);
        /*MMpull (m);*/
        MMpush (m, NIL);
        return 0;
    }
    MMechostr (MSKDEBUG, "_sqliteOpenFileEx :  database \" %s \" opened ...\n", name);
    return 0;
}



/*
    Scol function sqliteOpenFile : fun [Chn P I] ObjSqlite
    Ouvre une connection sur base fichier / Connect a file database
*/
/*int sqliteOpenFile (mmachine m) // old function
{
    MMechostr (MSKDEBUG, "sqliteOpenFile : entering ...\n");

    int mchannel, mfilename, mflag;
    char* name;
    int res;
    int flag = SQLITE_OPEN_READWRITE;

    mflag = MTOI (MMget (m, 0));
    mfilename = MTOP (MMget (m, 1));
    mchannel = MMget (m, 2)>>1;

    if ((mchannel == NIL) || (mfilename == NIL))
    {
        MMechostr(0, "sqliteOpenFile error : channel or filename is nil\n");
        MMpull (m); //MMpull (m); MMpull (m);
        MMset (m, 0, NIL);
        return 0;
    }

    if (mflag == SQLITE_ONLYREAD) flag = SQLITE_OPEN_READONLY;

    name = (char*) MMstartstr (m, mfilename);

    if ((strchr (name, ':')) == 0)         // reserved keywords in SQLite3 and greater
    {
        MMechostr (0, "sqliteOpenFile error :  the filename content a forbidden character (\":\") ... \n");
        MMpull (m); //MMpull (m); MMpull (m);
        MMset (m, 0, NIL);
        return 0;
    }
    // MMechostr (0, "_sqliteOpen error :  the filename is %s \n", name);
    res = sqliteOpen (m, name, flag);

    if (res != SQLITE_OK)
    {
        MMechostr (0, "sqliteOpenFile error :  can not open the database. Error code is : %d ...\n", res);
        MMpull (m); //MMpull (m); MMpull (m);
        MMset (m, 0, NIL);
        return 0;
    }
    MMechostr (0, "sqliteOpenFile :  database \" %s \" opened ...\n", name);
    //MMpush (m, 0);
    return 0;
}*/


/**
 * \brief Scol function _sqliteOpenFile : fun [Chn P] ObjSqlite
 * Open a new connection (database in a file)
 * Ouvre une connection sur base fichier / Connect a file database
 *
 * \param Chn : a channel
 * \param P : a read reference file
 * \return ObjSqlite : a new object or nil if error
*/
int _sqliteOpenFile (mmachine m)
{
    int mchannel, mfilename;
    char* name;
    int res;
    int flag = SQLITE_OPEN_READWRITE+SQLITE_OPEN_FULLMUTEX+SQLITE_OPEN_SHAREDCACHE;

    MMechostr (MSKDEBUG, "_sqliteOpenFile : entering ...\n");

    mfilename = MMpull (m);
    mchannel = MMget (m, 0);

    if ((mchannel == NIL) || (mfilename == NIL))
    {
        MMechostr(0, "_sqliteOpenFile error : channel or filename is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    name = (char*) MMstartstr (m, MTOP (mfilename));

    if ((strchr (name, ':')) != NULL)         /* reserved keywords in SQLite3 and greater*/
    {
        MMechostr (0, "_sqliteOpenFile error :  the filename content a forbidden character (%c) ... %s\n", ':', name);
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    /* MMechostr (0, "_sqliteOpen error :  the filename is %s \n", name);*/
    res = sqliteOpen (m, name, flag);

    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteOpenFile error :  can not open the database. Error code is : %d ...\n", res);
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    MMechostr (MSKDEBUG, "_sqliteOpenFile :  database \" %s \" opened ...\n", name);
    return 0;
}


/**
 * \brief Scol function _sqliteOpenMemory : fun [Chn] ObjSqlite
 * Create and connect a database into the memory
 *
 * \param Chn : a channel
 * \return ObjSqlite : a new object or nil if error
*/
int _sqliteOpenMemory (mmachine m)
{
    int mchannel;
    int flag = SCOL_SQLITE_READWRITE;
    char* name = ":memory:";
    int res;

    MMechostr (MSKDEBUG, "_sqliteOpenMemory : entering ...\n");

    mchannel    = MMget (m, 0);

    if (mchannel == NIL)
    {
        MMechostr(0, "_sqliteOpenMemory error : channel or filename is nil\n");
        MMpull (m);
        MMset (m, 0, NIL);
        return 0;
    }

    res = sqliteOpen (m, name, flag);

    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteOpenMemory error :  can not create the database. Error code is : %d ...\n", res);
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    MMechostr (MSKDEBUG, "_sqliteOpenMemory :  database created to the memory ...\n", name);

    return 0;
}

/**
 * \brief Scol function _sqliteOpenTemp : fun [Chn] ObjSqlite
 * Create and connect a database to the temporarly file
 *
 * \param Chn : a channel
 * \return ObjSqlite : a new object or nil if error
*/
int _sqliteOpenTemp (mmachine m)
{
    int mchannel;
    int flag = SCOL_SQLITE_READWRITE;
    char* name = "";
    int res;

    MMechostr (MSKDEBUG, "_sqliteOpenTemp : entering ...\n");

    mchannel    = MMget (m, 0);

    if (mchannel == NIL)
    {
        MMechostr(0, "_sqliteOpenTemp error : channel or filename is nil\n");
        MMpull (m);
        MMset (m, 0, NIL);
        return 0;
    }

    res = sqliteOpen (m, name, flag);

    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteOpenTemp error :  can not create the database. Error code is : %d ...\n", res);
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    MMechostr (MSKDEBUG, "_sqliteOpenTemp :  database created into a temp file ...\n", name);

    return 0;
}







/**
 * \brief Scol function _sqliteClose : fun [ObjSqlite] I
 * Close any database connexion
 *
 * \param ObjSqlite : a valid object
 * \return I : 0 if ok, nil if scol error or an other integer if sqlite error
*/
int _sqliteClose (mmachine m)
{
    int mdb;
    int res;
    sqlite3 *db;

    MMechostr (MSKDEBUG, "_sqliteClose : entering ...\n");

    mdb = MMpull (m);
    if (mdb == NIL)
    {
        MMechostr (0, "_sqliteClose : object \"ObjSQLite\" is nil...\n");
        MMpush (m, NIL);
        return 0;
    }

    db = (sqlite3*) MMfetch (m, MTOP (mdb), OBJSQLITE_HANDLE);

    if ((int) db == 0)
    {
        MMechostr (0, "_sqliteClose : object \"ObjSQLite\" is nil...\n");
        MMpush (m, NIL);
        return 0;
    }

    /*int cb = OBJbeginreflex (m, ObjSqliteType, (int) db, SCOL_SQLITE_CBPROGRESS);
    if (cb) sqlite3_progress_handler (db, 0, 0, 0);*/

    res = sqlite3_close (db);
    /* db = NULL;*/

    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteClose : close database return error  number : %d \n", res);
        MMpush (m, ITOM (res));
        return 0;
    }

    OBJdelTM (m, ObjSqliteType, PTOM (mdb));
    MMpush (m, ITOM (0));
    /*MMechostr (MSKDEBUG, "_sqliteClose : ok, finished\n");*/
    return 0;
}







/*
    Scol function _sqliteCallbackProgress : fun [ObjSqlite fun [ObjSqlite u0] I u0 I] ObjSqlite
    Register a callback (progress ...)
*/
int _sqliteCallbackProgress (mmachine m)
{
    int mlength, mdb;
    sqlite3 * db;
    int k;
    int muparam;
    int mrfl;

    int (*xprogress)(int *);

    MMechostr (MSKDEBUG, "_sqliteCallbackProgress : entering ...\n");

    mlength = MTOI (MMpull (m));
    mdb = MTOP (MMget (m, 2));

    db = (sqlite3*) MMfetch (m, mdb, OBJSQLITE_HANDLE);
    if ((int) db == NIL)
    /*if (db == NULL)*/
    {
        MMechostr (0, "_sqliteCallbackProgress : error database not found\n");
        MMpull (m);
        MMpush (m, NIL);
        return 1;
    }

    k = OBJaddreflex (m, ObjSqliteType, SCOL_SQLITE_CBPROGRESS);

    muparam = MMpull (m);
    mrfl = MTOP (MMpull (m));

    if (mrfl == NIL)
        xprogress = NULL;
    else
        xprogress = callobjsqlitecallbackProgress;

    /* arg 0 : db, 1 : nOps (déclenchée à chaque nb de fois défini par nOps), 2 : callback, 3 : param
    // pas certain toutefois, doc hyper floue et  exemples sur le net pas clairs
    // sqlite3_progress_handler (db, mlength, callobjsqlitecallbackProgress, (int) m);*/
    sqlite3_progress_handler (db, mlength, callobjsqlitecallbackProgress, (mmachine) m);

    return k;
}


/*
    Scol function _sqliteCallbackExec : fun [ObjSqlite fun [ObjSqlite u0 S S] I u0] ObjSqlite
    Register a callback (called to the sql execution)
*/
int _sqliteCallback (mmachine m)
{
    int mdb;
    sqlite3 *db;

    MMechostr (MSKDEBUG, "_sqliteCallbackExec : entering ...\n");

    mdb = MMget (m, 2);
    if (mdb == NIL)
    {
        MMechostr (MSKDEBUG, "_sqliteCallbackExec : object Sqlite doesn't exist\n");
        MMpull (m); MMpull (m); /* dépile deux sur trois, reste donc l'objet Scol ObjSqlite qui est retourné :)*/
        return 1;
    }
    db = (sqlite3*) MMfetch (m, MTOP (mdb), OBJSQLITE_HANDLE);
    /*MMpush (m, MMget (m, 1));   // callback
    //MMpush (m, MMget (m, 0));   // user param*/
    return OBJaddreflex (m, ObjSqliteType, SCOL_SQLITE_CB);
}

/*
    appelle la callback définie dans le package Scol
    value : valeur retournée
    column : nom de la colonne de la valeur retournée
*/
static int callobjsqlitecallback (mmachine m, const char * value, const char * column)
{
    int k;
    /*int l = strlen (column);
    int len = (l + 4) >>2;
    int buf = MMmalloc ((mmachine) m, len + 1, TYPEBUF);
    if (buf == NIL) return MERRMEM;
    //m->tape[buf+SizeHeader] = l;
    char* p = (int*)&m->tape[buf+SizeHeader+1];
    memcpy ((void*) p, (void*) column, l);
    p[l] = 0;
    //m->top[m->pp +1] = buf+buf+1;
    SAFEdelete (buf);

    int l2 = strlen (value);
    int len2 = (l2 + 4) >>2;
    int buf2 = MMmalloc ((mmachine) m, len2 + 1, TYPEBUF);
    if (buf2 == NIL) return MERRMEM;
    //m->tape[buf2+SizeHeader] = l2;
    char* p2 = (int*)&m->tape[buf2+SizeHeader+1];
    memcpy ((void*) p2, (void*) value, l2);
    p2[l2] = 0;
    //m->top[m->pp +1] = buf2+buf2+1;
    SAFEdelete (buf2);

    if (Mpushstrbloc (m, (char*) p)) return MERRMEM;
    if (Mpushstrbloc (m, (char*) p2)) return MERRMEM;
    */
    MMechostr (MSKDEBUG, "callobjsqlitecallback : entering ...\n");
/*
MMechostr (MSKDEBUG, "callobjsqlitecallback : attr =  %s...\n", (char*) column);
MMechostr (MSKDEBUG, "callobjsqlitecallback : value = %s ...\n", (char*) value);
*/
    if (Mpushstrbloc (m, (char*) column)) return MERRMEM;
    if (Mpushstrbloc (m, (char*) value)) return MERRMEM;

    if ((k = OBJcallreflex (m, 2))) {  return k; };

    return 0;
}


int callobjsqlitecallbackProgress (mmachine m)
{
    int k, cb;
    int mdb;
    sqlite3 * db;

    MMechostr (MSKDEBUG, "callobjsqlitecallbackProgress : entering ...\n");

    mdb = MTOP (MMget (m, 0));
    db = (sqlite3*) MMfetch (m, mdb, OBJSQLITE_HANDLE);

    cb = OBJbeginreflex (m, ObjSqliteType, (int) db, SCOL_SQLITE_CBPROGRESS);
    if (cb)
    {
        MMechostr (0, "callobjsqlitecallbackProgress : no callback 'PROGRESS' defined, error %d\n", cb);
        return 0;
    }

    if ((k = OBJcallreflex (m, 0))) return k;
    /*
    Doit retourner 0 pour que la requête en cours puisse continuer. Si la callback renvoie
    une valeur autre que 0, elle envoie automatiquement un signal d'interruption à SQLite
    (_sqlite3_interrupt) : pratique pour arrêter l'exécution en cours (bouton Annuler / Cancel
    par exemple) mais fait crasher Scol pour l'instant ...
    Donc à voir comment résoudre cette difficulté
    */
    return 0;
}

/*
    Function called to each sql execution
*/
static int objsqlitecallback (void* m2m, int nbcolumns, char **columntext, char **columnname)
{
    int i;
    int cb, mdb;
    sqlite3 *db;
    mmachine m;

    MMechostr (MSKDEBUG, "objsqlitecallback : entering ...\n");

    m = (mmachine) m2m;

    mdb = MTOP (MMget (m, 0));
    /*mdb = MTOP (MMpull (m));*/
    db = (sqlite3*) MMfetch (m, mdb, OBJSQLITE_HANDLE);

    for (i=0; i < nbcolumns; i++)
    {
        cb = OBJbeginreflex (m, ObjSqliteType, (int) db, SCOL_SQLITE_CB);
        if (cb)
            {
            MMechostr (0, "objsqlitecallback : no callback 'EXEC' defined, error %d\n", cb);
            MMpull (m);
            MMpush (m, PTOM (mdb));
            return 0;
            }
        callobjsqlitecallback (m, columntext[i], columnname[i]);
    }
    /*MMpush (m, PTOM (mdb));*/
    return 0;
}


/**
 * \brief Scol function _sqliteExec : fun [ObjSqlite S I] I
 * Execute any sql instruction
 * Return 0 if ok or the sqlite's error code
 *
 * \param ObjSqlite : a valid object
 * \param S : a valid sqlite request
 * \param I : should be nil
 * \return I : 0 if success or the error code
*/
int _sqliteExec (mmachine m)
{
    int mdb, msql, mpaf;
    const char *reqsql;
    int res;
    sqlite3 * db;
    char * err = NULL;

    MMechostr (MSKDEBUG, "_sqliteExec : entering ...\n");

    mpaf = MTOI (MMpull (m));
    msql = MTOP (MMpull (m));
    mdb = MMget (m, 0);

    if (mdb == NIL)
    {
        MMechostr (0, "_sqliteExec : object Sqlite doesn't exist\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    db = (sqlite3 *) MMfetch (m, MTOP (mdb), OBJSQLITE_HANDLE);

    reqsql = (char*) MMstartstr (m, msql);
    if (db == NULL)
    {
        MMechostr (0, "_sqliteExec : error database not found\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    /* res = sqlite3_exec (db, reqsql, objsqlitecallback, (int) m, &err);*/
    res = sqlite3_exec (db, reqsql, objsqlitecallback, (mmachine) m, &err);
    MMpull (m);
    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteExec : error sql execution \"%s\" : %d\n", reqsql, res);
        MMechostr (0, "_sqliteExec : error sql execution \"%s\" : %s\n", reqsql, err);
        sqlite3_free (err);
        MMpush (m, (int) res*2);
        return 0;
    }

    MMpush (m, 0);
    return 0;
}





static int objsqlitecallbackexecresult (void* m2m, int nbcolumns, char **columntext, char **columnname)
{
    mmachine m = (mmachine) m2m;
    int i;

    MMechostr (MSKDEBUG, "objsqlitecallbackexecresult : entering ... %d\n", nbcolumns);

    for (i=0; i < nbcolumns; i++)   /* create tuple [S S] */
    {
        if (columntext[i] == NULL)
        {
            Mpushstrbloc (m, (char *) columnname[i]);
            MMpush (m, NIL);
            MMpush (m, ITOM (2));
            MBdeftab (m);
            continue;
        }
        if (!strcmp(columnname[i], ""))
            MMpush (m, NIL);
        else
            Mpushstrbloc (m, (char *) columnname[i]);
        if (!strcmp(columntext[i], ""))
            MMpush (m, NIL);
        else
            Mpushstrbloc (m, (char *) columntext[i]);
        MMpush (m, ITOM (2));
        MBdeftab (m);
    }
    MMpush (m, NIL);
    for (i=0; i < nbcolumns; i++)
    {
        MMpush (m, ITOM (2));
        MBdeftab (m);
    }

    MMsetglobal (m, 0, 1+MMgetglobal (m, 0));
    /*MMsetglobal (m, 0, nbcolumns);*/
    return 0;
}


/**
 * \brief Scol function _sqliteExecResult : fun [ObjSlite S I] [[S S] r1]
 * Run a sqlite request and return the result without callback
 *
 * \param ObjSqlite : a valid object
 * \param S : a valid sqlite request
 * \param I : should be nil
 * \return [[S S] r1] : the result or nil if error
*/
int _sqliteExecResult (mmachine m)
{
    int mdb, msql, mpaf;
    const char *reqsql;
    sqlite3 * db;
    char *err = NULL;

    int res;
    int i, j;

    MMechostr (MSKDEBUG, "_sqliteExecResult : entering ...\n");

    mpaf = MTOI (MMpull (m));
    msql = MTOP (MMpull (m));
    mdb = MMpull (m);

    if (mdb == NIL)
    {
        MMechostr (0, "_sqliteExecResult : object Sqlite doesn't exist\n");
        MMpush (m, NIL);
        return 0;
    }

    db = (sqlite3 *) MMfetch (m, MTOP (mdb), OBJSQLITE_HANDLE);
    if (db == NULL)
    {
        MMechostr (0, "_sqliteExecResult : error database not found\n");
        MMpush (m, NIL);
        return 0;
    }

    reqsql = (char*) MMstartstr (m, msql);
    MMsetglobal (m, 0, 0);

    res = sqlite3_exec (db, reqsql, objsqlitecallbackexecresult, (mmachine*) m, &err);
    if (res != SQLITE_OK)
    {
        MMechostr (0, "_sqliteExecResult : error sql execution \"%s\" : %d\n", reqsql, res);
        MMechostr (0, "_sqliteExecResult : error sql execution \"%s\" : %s\n", reqsql, err);
        sqlite3_free (err);
        MMpush (m, NIL);
        return 0;
    }

    j = MMgetglobal (m, 0);
    if (!j)
    {
        MMpush (m, NIL);
        return 0;
    }
    MMpush (m, NIL);   /* create list [[S S] r1] */
    for (i=0; i < j; i++)
    {
        MMpush (m, ITOM (2));
        MBdeftab (m);
    }
    return 0;
}





/*ok, il y a plus simple mais je préfère ... :)*/
int _getsqlitelimitflag (int mcat)
{
    int cat;

    MMechostr (MSKDEBUG, "_getsqlitelimitflag : entering ...\n");

    switch (mcat)
    {
        case SCOL_SQLITE_LENGTH_LIMIT :
        {
            cat = SQLITE_LIMIT_LENGTH;
            break;
        }
        case SCOL_SQLITE_SQL_LENGTH_LIMIT :
        {
            cat = SQLITE_LIMIT_SQL_LENGTH;
            break;
        }
        case SCOL_SQLITE_COLUMN_LIMIT :
        {
            cat =  SQLITE_LIMIT_COLUMN;
            break;
        }
        case SCOL_SQLITE_EXPR_DEPTH_LIMIT :
        {
            cat = SQLITE_LIMIT_EXPR_DEPTH;
            break;
        }
        case SCOL_SQLITE_COMPOUND_SELECT_LIMIT :
        {
            cat = SQLITE_LIMIT_COMPOUND_SELECT;
            break;
        }
        case SCOL_SQLITE_VDBE_OP_LIMIT :
        {
            cat = SQLITE_LIMIT_VDBE_OP;
            break;
        }
        case SCOL_SQLITE_FUNCTION_ARG_LIMIT :
        {
            cat = SQLITE_LIMIT_FUNCTION_ARG;
            break;
        }
        case SCOL_SQLITE_ATTACHED_LIMIT :
        {
            cat = SQLITE_LIMIT_ATTACHED;
            break;
        }
        case SCOL_SQLITE_LIKE_PATTERN_LENGTH_LIMIT :
        {
            cat = SQLITE_LIMIT_LIKE_PATTERN_LENGTH;
            break;
        }
        case SCOL_SQLITE_VARIABLE_NUMBER_LIMIT :
        {
            cat = SQLITE_LIMIT_VARIABLE_NUMBER;
            break;
        }
        case SCOL_SQLITE_TRIGGER_DEPTH_LIMIT :
        {
            cat = SQLITE_LIMIT_TRIGGER_DEPTH;
            break;
        }
        default :
        {
            cat = -1;
        }
    }
    return cat;
}

/**
 * \brief _sqliteGetSizeLimit : Get the maximale size to objects manipulate by SQLite3
 * fun [ObjSqlite I] I
 * more informations : _sqliteSetSizeLimit
 *
 * \param ObjSqlite : a valid object
 * \param I : flag
 * \return I : the value
 */
int _sqliteGetSizeLimit (mmachine m)
{
    int mcat;
    int mdb;
    int cat;
    int res;
    sqlite3 * db;

    MMechostr (MSKDEBUG, "_sqliteGetSizeLimit : entering ...\n");

    mcat = MTOI (MMpull (m));
    mdb = MMpull (m);

    db = (sqlite3 *) MMfetch (m, MTOP(mdb), OBJSQLITE_HANDLE);

    if (db == NULL)
    {
        MMechostr (0, "_sqliteGetSizeLimit : error database not found\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    cat = _getsqlitelimitflag (mcat);
    if (cat == NIL)
    {
        MMechostr (0, "_sqliteGetSizeLimit : the given flag seems incorrect\n");
        MMpush (m, NIL);
        return 1;
    }

    res = sqlite3_limit (db, cat, -1);
    MMpush (m, res*2);
    return 0;
}

/**
 * \brief _sqliteSetSizeLimit : Set the maximale size to objects manipulate by SQLite3
 * fun [ObjSqlite I I] I
 * more informations : http://www.sqlite.org/c3ref/c_limit_attached.html
 *
 * \param ObjSqlite : a valid object
 * \param I : flag : SQLITE_LENGTH_LIMIT, SQLITE_SQL_LENGTH_LIMIT, SQLITE_COLUMN_LIMIT, SQLITE_EXPR_DEPTH_LIMIT, SQLITE_COMPOUND_SELECT_LIMIT, SQLITE_VDBE_OP_LIMIT, SQLITE_FUNCTION_ARG_LIMIT, SQLITE_ATTACHED_LIMIT, SQLITE_LIKE_PATTERN_LENGTH_LIMIT, SQLITE_VARIABLE_NUMBER_LIMIT, SQLITE_TRIGGER_DEPTH_LIMIT
 * \param I : the new value
 * \return I : this new value
 */
int _sqliteSetSizeLimit (mmachine m)
{
    int mnewvalue;
    int mcat;
    int mdb;
    int cat;
    sqlite3 * db;

    MMechostr (MSKDEBUG, "_sqliteSetSizeLimit : entering ...\n");

    mnewvalue = MTOI (MMpull (m));
    mcat = MTOI (MMpull (m));
    mdb = MTOP (MMpull (m));

    db = (sqlite3 *) MMfetch (m, mdb, OBJSQLITE_HANDLE);

    if (db == NULL)
    {
        MMechostr (0, "_sqliteSetSizeLimit : error database not found\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    cat = _getsqlitelimitflag (mcat);
    if (cat == NIL)
    {
        MMechostr (0, "_sqliteSetSizeLimit : the given flag seems incorrect\n");
        MMpush (m, NIL);
        return 1;
    }

    /*int res = */sqlite3_limit (db, cat, mnewvalue);
    MMpush (m, mnewvalue*2);
    return 0;
}


/**
 * \brief Scol function _sqliteThreadsafe : fun [] I
 * Return 0 if mono-thread, 1 if serialized, 2 if multi-thread (strictly defined at the compile-time)
*/
int _sqliteThreadsafe (mmachine m)
{
    MMechostr (MSKDEBUG, "_sqliteThreadsafe : entering ... %d\n", sqlite3_threadsafe());

    MMpush (m, 2*sqlite3_threadsafe());
    return 0;
}


/**
 * \brief Scol function _sqliteShraedCacheEnabled : fun [I] I
 * Return 0 if success or a specific error code
*/
int _sqliteShraedCacheEnabled (mmachine m)
{
    int mbool;
    bool b;
    int res;

    MMechostr (MSKDEBUG, "_sqliteShraedCacheEnabled : entering ...\n");

    mbool = MTOI (MMpull (m));
    b = false;
    if (mbool != 0) b = true;
    res = sqlite3_enable_shared_cache (b);

    MMpush (m, res*2);
    return 0;
}



/**
 * \brief Scol function _sqliteVersion : fun [] S
 * Return the SQLITE used version
*/
int _sqliteVersion (mmachine m)
{
    MMechostr (MSKDEBUG, "_sqliteVersion : entering ...\n");
    return Mpushstrbloc (m, SQLITE_VERSION);
}

/**
 * \brief Scol function _sqliteVersionScol : fun [] S
 * Return this library version (it is defined in main.h)
*/
int _sqliteVersionScol (mmachine m)
{
    MMechostr (MSKDEBUG, "_sqliteVersionScol : entering ...\n");
    return Mpushstrbloc (m, SCOL_SQLITE_VERSION);
}



/* Flags */
int SC_SQLITE_ONLYREAD (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_ONLYREAD)); return 0; }
int SC_SQLITE_READWRITE (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_READWRITE)); return 0; }
int SC_SQLITE_OPEN_NOMUTEX (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_OPEN_NOMUTEX)); return 0; }
int SC_SQLITE_OPEN_FULLMUTEX (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_OPEN_FULLMUTEX)); return 0; }
int SC_SQLITE_OPEN_SHAREDCACHE (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_OPEN_SHAREDCACHE)); return 0; }
int SC_SQLITE_OPEN_PRIVATE_CACHE (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_OPEN_PRIVATE_CACHE)); return 0; }

int SC_SQLITE_LENGTH_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_LENGTH_LIMIT)); return 0; }
int SC_SQLITE_SQL_LENGTH_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_SQL_LENGTH_LIMIT)); return 0; }
int SC_SQLITE_COLUMN_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_COLUMN_LIMIT)); return 0; }
int SC_SQLITE_EXPR_DEPTH_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_EXPR_DEPTH_LIMIT)); return 0; }
int SC_SQLITE_COMPOUND_SELECT_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_COMPOUND_SELECT_LIMIT)); return 0; }
int SC_SQLITE_VDBE_OP_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_VDBE_OP_LIMIT)); return 0; }
int SC_SQLITE_FUNCTION_ARG_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_FUNCTION_ARG_LIMIT)); return 0; }
int SC_SQLITE_ATTACHED_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_ATTACHED_LIMIT)); return 0; }
int SC_SQLITE_LIKE_PATTERN_LENGTH_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_LIKE_PATTERN_LENGTH_LIMIT)); return 0; }
int SC_SQLITE_VARIABLE_NUMBER_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_VARIABLE_NUMBER_LIMIT)); return 0; }
int SC_SQLITE_TRIGGER_DEPTH_LIMIT (mmachine m) { MMpush (m, ITOM (SCOL_SQLITE_TRIGGER_DEPTH_LIMIT)); return 0; }



/* API définitions : */

char* sqlite_name[SQLITE_PKG_NB]=
{
        "ObjSqlite",

        "SQLITE_ONLYREAD", "SQLITE_READWRITE",
        "SQLITE_OPEN_NOMUTEX", "SQLITE_OPEN_FULLMUTEX",
        "SQLITE_OPEN_SHAREDCACHE", "SQLITE_OPEN_PRIVATE_CACHE",

        "SQLITE_LENGTH_LIMIT", "SQLITE_SQL_LENGTH_LIMIT",
        "SQLITE_COLUMN_LIMIT", "SQLITE_EXPR_DEPTH_LIMIT",
        "SQLITE_COMPOUND_SELECT_LIMIT", "SQLITE_VDBE_OP_LIMIT",
        "SQLITE_FUNCTION_ARG_LIMIT", "SQLITE_ATTACHED_LIMIT",
        "SQLITE_LIKE_PATTERN_LENGTH_LIMIT", "SQLITE_VARIABLE_NUMBER_LIMIT",
        "SQLITE_TRIGGER_DEPTH_LIMIT",

        "_sqliteOpenFileEx",
        "_sqliteOpenFile",
        "_sqliteOpenMemory",
        "_sqliteOpenTemp",
        "_sqliteClose",
        "_sqliteCallbackExec",
        "_sqliteExec",
        "_sqliteVersion",
        "_sqliteVersionScol",
        "_sqliteThreadsafe",
        "_sqliteSharedCacheEnabled",
        "_sqliteGetSizeLimit",
        "_sqliteSetSizeLimit",
        "_sqliteCallbackProgress",
        "_sqliteExecResult"
};


int (*sqlite_fun[SQLITE_PKG_NB])(mmachine m)=
{
        NULL,

        SC_SQLITE_ONLYREAD, SC_SQLITE_READWRITE,
        SC_SQLITE_OPEN_NOMUTEX, SC_SQLITE_OPEN_FULLMUTEX,
        SC_SQLITE_OPEN_SHAREDCACHE, SC_SQLITE_OPEN_PRIVATE_CACHE,

        SC_SQLITE_LENGTH_LIMIT, SC_SQLITE_SQL_LENGTH_LIMIT,
        SC_SQLITE_COLUMN_LIMIT, SC_SQLITE_EXPR_DEPTH_LIMIT,
        SC_SQLITE_COMPOUND_SELECT_LIMIT, SC_SQLITE_VDBE_OP_LIMIT,
        SC_SQLITE_FUNCTION_ARG_LIMIT, SC_SQLITE_ATTACHED_LIMIT,
        SC_SQLITE_LIKE_PATTERN_LENGTH_LIMIT, SC_SQLITE_VARIABLE_NUMBER_LIMIT,
        SC_SQLITE_TRIGGER_DEPTH_LIMIT,

        _sqliteOpenFileEx,
        _sqliteOpenFile,
        _sqliteOpenMemory,
        _sqliteOpenTemp,
        _sqliteClose,
        _sqliteCallback,
        _sqliteExec,
        _sqliteVersion,
        _sqliteVersionScol,
        _sqliteThreadsafe,
        _sqliteShraedCacheEnabled,
        _sqliteGetSizeLimit,
        _sqliteSetSizeLimit,
        _sqliteCallbackProgress,
        _sqliteExecResult
};


int sqlite_narg[SQLITE_PKG_NB]=
{
        TYPTYPE,

        0, 0,
        0, 0,
        0, 0,

        0, 0,
        0, 0,
        0, 0,
        0, 0,
        0, 0,
        0,

        5,
        2,
        1,
        1,
        1,
        3,
        3,
        0,
        0,
        0,
        1,
        2,
        3,
        4,
        3
};

char* sqlite_type[SQLITE_PKG_NB]=
{
        NULL,

        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",

        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",
        "fun [] I", "fun [] I",
        "fun [] I",

        "fun [Chn P I I I] ObjSqlite",
        "fun [Chn P] ObjSqlite",
        "fun [Chn] ObjSqlite",
        "fun [Chn] ObjSqlite",
        "fun [ObjSqlite] I",
        "fun [ObjSqlite fun [ObjSqlite u0 S S] I u0] ObjSqlite",
        "fun [ObjSqlite S I] I",
        "fun [] S",
        "fun [] S",
        "fun [] I",
        "fun [I] I",
        "fun [ObjSqlite I] I",
        "fun [ObjSqlite I I] I",
        "fun [ObjSqlite fun [ObjSqlite u0] I u0 I] ObjSqlite",
        "fun [ObjSqlite S I] [[[S S] r1] r1]"
};


int ObjSqliteDestroy (mmachine m, int handsys, int mobj)
{
    sqlite3 *obj;
    MMechostr(MSKDEBUG, "ObjSqliteDestroy: destroying\n");

    obj = (sqlite3*) MMfetch (m, MTOP (mobj), OBJSQLITE_HANDLE);
    if (obj == NULL)
    {
        MMechostr(MSKDEBUG, "ObjSqliteDestroy: object already destroyed\n");
        MMset (m, 0, NIL);
        return 0;
    }
    sqlite3_close (obj);
    /*SAFEdelete (obj);*/
    MMstore (m, MTOP (mobj), OBJSQLITE_HANDLE, (int) NULL);

    MMechostr(MSKDEBUG, "ObjSqliteDestroy: object has been destroyed\n");
    return 0;
}



/*

    Fonctions de chargement / déchargement de la librairie / bibliothèque

    - Version MS Windows
    - Version GNU / Linux
    - Version Apple MacOS X : à faire ... :)

*/

/*
  Fonction de chargement de la librairie
*/
int ScolLoadPlugin (mmachine m, cbmachine w)
{
    int k = 0;
    ww = w;
    
    MMechostr (0, "\nSQLITE3 Support : loading ...\n");

    k = PKhardpak(m, "SQLITEengine", SQLITE_PKG_NB, sqlite_name, sqlite_fun, sqlite_narg, sqlite_type);
    ObjSqliteType  = OBJregister (SQLITE_RFL_NB, 1, ObjSqliteDestroy, "ObjSqliteType");
    return k;
}

/*
  Fonction de déchargement de la librairie
*/
int ScolUnloadPlugin (void)
{
    return 0;
}

