#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>
#include <uthash/utstring.h>

#include "log.h"
#include "libuv_dbus.h"
#include "skins.h"
#include "log.h"

typedef struct 
{
    sqlite3_vtab 		vTable;
    sqlite3 			*pSqlDb;
    char                *pTblName;
} SKINRES_VTBL, *PSKINRES_VTBL;

typedef struct 
{
    sqlite3_vtab_cursor base;
    int             count;
    int             eof;
} SKINRES_CURSOR, *PSKINRES_CURSOR;

static int __skin_res_destructor(sqlite3_vtab *pVtab)
{
    PSKINRES_VTBL p = (PSKINRES_VTBL)pVtab;

    if(p->pTblName != NULL)
    {
        free(p->pTblName);
        p->pTblName = NULL;
    }
    sqlite3_free(p);

    return 0;
}

static int __skin_res_create(sqlite3 *pDb,
                     void *pAux,
                     int argc, const char * const *argv,
                     sqlite3_vtab **pp_vt,
                     char **pzErr)
{
    UT_string *pSqlCmd;
    int rc = SQLITE_OK;
    PSKINRES_VTBL pVTbl;

    /* Allocate the sqlite3_vtab/example_vtab structure itself */
    pVTbl = (PSKINRES_VTBL)sqlite3_malloc(sizeof(SKINRES_VTBL));

    if(pVTbl == NULL)
    {
        return SQLITE_NOMEM;
    }
    
    pVTbl->pSqlDb   = pDb;
    pVTbl->pTblName = strdup(argv[2]);
    
    utstring_new(pSqlCmd);
	if(strcmp(argv[0], RES_MODE_NAME) == 0)
	{
		utstring_printf(pSqlCmd, CREATE_RES_TBL_SQL, "");
	}
	else 
	{
		utstring_printf(pSqlCmd, CREATE_SKIN_TBL_SQL, "");
	}

    /* Declare the vtable's structure */
    rc = sqlite3_declare_vtab(pDb, utstring_body(pSqlCmd));
    utstring_free(pSqlCmd);

    if(rc != SQLITE_OK)
    {
        __skin_res_destructor((sqlite3_vtab*)pVTbl);
        
        return SQLITE_ERROR;
    }

    /* Success. Set *pp_vt and return */
    *pp_vt = &pVTbl->vTable;

    return SQLITE_OK;
}

static int __skin_res_connect( sqlite3 *db, void *p_aux,
                       int argc, const char * const *argv,
                       sqlite3_vtab **pp_vt, char **pzErr )
{
    return __skin_res_create(db, p_aux, argc, argv, pp_vt, pzErr);
}

static int __skin_res_disconnect(sqlite3_vtab *pVtab)
{
    return __skin_res_destructor(pVtab);
}

static int __skin_res_destroy(sqlite3_vtab *pVtab)
{
    int rc = SQLITE_OK;
    //PSKINRES_VTBL p = (PSKINRES_VTBL)pVtab;

    if(rc == SQLITE_OK)
    {
        rc = __skin_res_destructor(pVtab);
    }

    return rc;
}

static int __skin_res_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
{
    PSKINRES_CURSOR pCur = (PSKINRES_CURSOR)sqlite3_malloc(sizeof(SKINRES_CURSOR));
    *ppCursor = (sqlite3_vtab_cursor*)pCur;

    return (pCur ? SQLITE_OK : SQLITE_NOMEM);
}

static int __skin_res_close(sqlite3_vtab_cursor *cur)
{
    PSKINRES_CURSOR pCur = (PSKINRES_CURSOR)cur;
    sqlite3_free(pCur);

    return SQLITE_OK;
}

static int __skin_res_eof(sqlite3_vtab_cursor *cur)
{
    return ((PSKINRES_CURSOR)cur)->eof;
}

static int __skin_res_next(sqlite3_vtab_cursor *pInCur)
{
    PSKINRES_CURSOR pCur    = (PSKINRES_CURSOR)pInCur;
    //PSKINRES_VTBL pvTable   = (PSKINRES_VTBL)pInCur->pVtab;

    /* Increment the current row count. */
    pCur->count += 1;

    /* Arbitrary contstraint: when we get to 10 rows, then stop. */
    if(pCur->count >= SkinsDefaultSize())
    {
        pCur->eof = 1;
    }

    return SQLITE_OK;
}

static int __skin_res_column(sqlite3_vtab_cursor *pInCur, sqlite3_context *ctx, int iCol)
{
    PSKINRES_CURSOR pCur    = (PSKINRES_CURSOR)pInCur;
    PSKIN_RES_INFO pItem    = SkinsItemById(pCur->count);
	//PSKINRES_VTBL pvTable   = (PSKINRES_VTBL)pInCur->pVtab;

    /* Just return the ordinal of the column requested. */
    switch(iCol)
    {
        case 0:
            sqlite3_result_int(ctx, pCur->count);
            break;

        case 1:
            sqlite3_result_text(ctx, pItem->pResVer, strlen(pItem->pResVer), SQLITE_STATIC);
            break;
           
        case 2:
            sqlite3_result_text(ctx, pItem->pLocalPath, strlen(pItem->pLocalPath), SQLITE_STATIC);
            break;

        case 3:
            sqlite3_result_text(ctx, pItem->pLocalPath, strlen(pItem->pLocalPath), SQLITE_STATIC);
            break;

        case 4:
            sqlite3_result_text(ctx, pItem->pMD5Chksum, strlen(pItem->pMD5Chksum), SQLITE_STATIC);
            break;
    }
    
    return SQLITE_OK;
}

static int __skin_cfg_column(sqlite3_vtab_cursor *pInCur, sqlite3_context *ctx, int iCol)
{
    PSKINRES_CURSOR pCur    = (PSKINRES_CURSOR)pInCur;
    PSKIN_RES_INFO pItem    = SkinsItemById(pCur->count);
	//PSKINRES_VTBL pvTable   = (PSKINRES_VTBL)pInCur->pVtab;

    /* Just return the ordinal of the column requested. */
    switch(iCol)
    {
        case 0:
            sqlite3_result_int(ctx, pCur->count);
            break;

        case 1:
            sqlite3_result_text(ctx, pItem->pKeyName, strlen(pItem->pKeyName), SQLITE_STATIC);
            break;
           
        case 2:
            sqlite3_result_int(ctx, pItem->resType);
            break;

        case 3:
            sqlite3_result_int(ctx, 0);
            break;

        case 4:
            sqlite3_result_int(ctx, pCur->count);
            break;

        case 5:
            sqlite3_result_text(ctx, "", 0, SQLITE_STATIC);
            break;
    }
    
    return SQLITE_OK;
}

static int __skin_res_rowid(sqlite3_vtab_cursor *pInCur, sqlite_int64 *p_rowid)
{
    PSKINRES_CURSOR pCur = (PSKINRES_CURSOR)pInCur;

    /* Just use the current row count as the rowid. */
    *p_rowid = pCur->count;

    return SQLITE_OK;
}

static int __skin_res_filter( sqlite3_vtab_cursor *pVtc, 
                      int idxNum, const char *idxStr,
                      int argc, sqlite3_value **argv )
{
    //int rc;
    //int i;

    /* Initialize the cursor structure. */
    PSKINRES_CURSOR pCur = (PSKINRES_CURSOR)pVtc;

    /* Zero rows returned thus far. */
    pCur->count          = 0;

    /* Have not reached end of set. */
    pCur->eof            = 0;

    /* Move cursor to first row. */
    return __skin_res_next(pVtc);
}

/* Pretty involved. We don't implement in this example. */
static int __skin_res_best_index(sqlite3_vtab *pVTbl, sqlite3_index_info *pIdxInfo)
{
    return SQLITE_OK;
}

static sqlite3_module g_ResModule =
{
    0,              		/* iVersion */
    __skin_res_create,      /* xCreate       - create a vtable */
    __skin_res_connect,     /* xConnect      - associate a vtable with a connection */
    __skin_res_best_index,  /* xBestIndex    - best index */
    __skin_res_disconnect,  /* xDisconnect   - disassociate a vtable with a connection */
    __skin_res_destroy,     /* xDestroy      - destroy a vtable */
    __skin_res_open,        /* xOpen         - open a cursor */
    __skin_res_close,       /* xClose        - close a cursor */
    __skin_res_filter,      /* xFilter       - configure scan constraints */
    __skin_res_next,        /* xNext         - advance a cursor */
    __skin_res_eof,         /* xEof          - inidicate end of result set*/
    __skin_res_column,      /* xColumn       - read data */
    __skin_res_rowid,       /* xRowid        - read data */
    NULL,           		/* xUpdate       - write data */
    NULL,           		/* xBegin        - begin transaction */
    NULL,           		/* xSync         - sync transaction */
    NULL,           		/* xCommit       - commit transaction */
    NULL,           		/* xRollback     - rollback transaction */
    NULL,           		/* xFindFunction - function overloading */
};

static sqlite3_module g_SkinModule =
{
    0,              		/* iVersion */
    __skin_res_create,      /* xCreate       - create a vtable */
    __skin_res_connect,     /* xConnect      - associate a vtable with a connection */
    __skin_res_best_index,  /* xBestIndex    - best index */
    __skin_res_disconnect,  /* xDisconnect   - disassociate a vtable with a connection */
    __skin_res_destroy,     /* xDestroy      - destroy a vtable */
    __skin_res_open,        /* xOpen         - open a cursor */
    __skin_res_close,       /* xClose        - close a cursor */
    __skin_res_filter,      /* xFilter       - configure scan constraints */
    __skin_res_next,        /* xNext         - advance a cursor */
    __skin_res_eof,         /* xEof          - inidicate end of result set*/
    __skin_cfg_column,      /* xColumn       - read data */
    __skin_res_rowid,       /* xRowid        - read data */
    NULL,           		/* xUpdate       - write data */
    NULL,           		/* xBegin        - begin transaction */
    NULL,           		/* xSync         - sync transaction */
    NULL,           		/* xCommit       - commit transaction */
    NULL,           		/* xRollback     - rollback transaction */
    NULL,           		/* xFindFunction - function overloading */
};

int InitSkinRomDatabase(sqlite3 *pDataBase)
{
    if((sqlite3_create_module(pDataBase, SKIN_MODE_NAME, &g_SkinModule, NULL) == SQLITE_OK)
			&& (sqlite3_create_module(pDataBase, RES_MODE_NAME, &g_ResModule, NULL) == SQLITE_OK))
	{
		return 0;
	}
	else
	{
		return -ERR_SQL_REG_MODULE;
	}
}