#include <stdio.h>
#include "kk_tsl_api.h"
#include "sqlite3.h"
#include "kk_log.h"
#include "kk_dm_mng.h"
#include "kk_property_db.h"

extern sqlite3 *g_kk_pDb;
typedef struct {
    void *mutex;
    sqlite3 *pDb;	
} kk_property_db_ctx_t;

typedef enum{
	DB_IDX = 0,
	DB_DEVICECODE,
	DB_IDENTIFITER,
	DB_VALUE,
	DB_VALUETYPE,
	DB_DEVTYPE
};

typedef enum{
	DB_LOCKKEY_IDX = 0,
	DB_LOCKKEY_DEVICECODE,
	DB_LOCKKEY_KEYID,
	DB_LOCKKEY_KEYTYPE,
	DB_LOCKKEY_KEYROLE,
	DB_LOCKKEY_ISVALID,
	DB_LOCKKEY_KEYNAME,
	DB_LOCKKEY_KEYEFFECTIVE,
	DB_LOCKKEY_KEYEXPIRE
};

static kk_property_db_ctx_t s_kk_property_db_ctx = {0};
static kk_property_db_ctx_t *_kk_property_db_get_ctx(void)
{
    return &s_kk_property_db_ctx;
}
static void _kk_property_db_lock(void)
{
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
    if (ctx->mutex) {
        HAL_MutexLock(ctx->mutex);
    }
}

static void _kk_property_db_unlock(void)
{
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
    if (ctx->mutex) {
        HAL_MutexUnlock(ctx->mutex);
    }
}
static int kk_property_db_Init(void)
{
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

    //eUtils_LockLock(&sLock);
    _kk_property_db_lock();
    ctx->pDb = g_kk_pDb;
  

    
	const char *pPrpertyTable = "CREATE TABLE IF NOT EXISTS PropertiesInfo( \
		idx INTEGER PRIMARY KEY, \
		deviceCode varchar(33), \
		identifier varchar(33), \
		value varchar(33), \
		valueType INTEGER, \
		devType INTEGER)";
    char *pcErr;
    
//     DBG_vPrintf(DBG_SQL, "Execute SQL: '%s'\n", pConditionTableDef);
    
    if (sqlite3_exec(ctx->pDb, pPrpertyTable, NULL, NULL, &pcErr) != SQLITE_OK)
    {
        ERROR_PRINT("Error creating table (%s)\n", pcErr);
        sqlite3_free(pcErr);
        //eUtils_LockUnlock(&sLock);
        _kk_property_db_unlock();
        return FAIL_RETURN;
    }
	 
	const char *pLockKeys = "CREATE TABLE IF NOT EXISTS LockKeysInfo( \
		idx INTEGER PRIMARY KEY, \
		deviceCode varchar(33), \
		KeyID varchar(10), \
		KeyType INTEGER, \
		KeyRole INTEGER, \
		IsValid INTEGER, \
		KeyName varchar(255), \
		KeyEffectiveTime INTEGER, \
		KeyExpiryTime INTEGER)";    
		
	if (sqlite3_exec(ctx->pDb, pLockKeys, NULL, NULL, &pcErr) != SQLITE_OK)
	{
		ERROR_PRINT("Error creating table (%s)\n", pcErr);
		sqlite3_free(pcErr);
		//eUtils_LockUnlock(&sLock);
		_kk_property_db_unlock();
		return FAIL_RETURN;
	}

    //eUtils_LockUnlock(&sLock);
    _kk_property_db_unlock();
    return SUCCESS_RETURN;
}

int kk_property_db_init(void)
{
	int res = 0;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
	
    /* Create Mutex */
    ctx->mutex = HAL_MutexCreate();
    if (ctx->mutex == NULL) {
        return FAIL_RETURN;
    }

	res = kk_property_db_Init();
	if(res != SUCCESS_RETURN){
		ERROR_PRINT("[%s][%d]kk_wlist_db_Init FAIL!!!\n",__FUNCTION__,__LINE__);

	}
	//_kk_load_subDevice();
	return SUCCESS_RETURN;
}
static int _kk_check_property_exist(const char* deviceCode,const char* identifier)
{
	int isExist = 0;
	sqlite3_stmt *stmt;
	char *pDeviceCode = NULL;
	char *pIdentifier = NULL;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
	const char *searchCmd = "select * from PropertiesInfo;";	
	_kk_property_db_lock();
	sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
	//INFO_PRINT("total_column = %d\n", sqlite3_column_count(stmt));
	while(sqlite3_step(stmt) == SQLITE_ROW){
	   pDeviceCode = sqlite3_column_text(stmt, DB_DEVICECODE);
	   pIdentifier = sqlite3_column_text(stmt, DB_IDENTIFITER);
	   if(!strcmp(deviceCode,pDeviceCode)&&!strcmp(identifier,pIdentifier))
	   
{
		 isExist = 1;
		 break;
	   }
    }
    //INFO_PRINT("\n");
	sqlite3_finalize(stmt);
	_kk_property_db_unlock();
	return isExist;
}

int kk_property_db_insert(const char *deviceCode,const char *identifier,kk_tsl_data_type_e valuetype,int devtype)
{
	const char *insertCmd = "insert into PropertiesInfo (deviceCode,identifier,value,valueType,devType) \
								values ('%s','%s','%s','%d','%d');";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	if(_kk_check_property_exist(deviceCode,identifier) == 1)
	{
		//WARNING_PRINT("[%s][%d] DATA ALREADY EXIST!!!\n",__FUNCTION__,__LINE__);
		return SUCCESS_RETURN;
	}
	_kk_property_db_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,devtype);	

	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   //INFO_PRINT("sub device insert data successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	return SUCCESS_RETURN;
}

int kk_property_db_update_value(const char *deviceCode,const char *identifier,const char* value)
{
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();

	sqlCmd = sqlite3_mprintf("UPDATE PropertiesInfo SET value='%s'  WHERE deviceCode= '%s' and identifier = '%s'",value,deviceCode,identifier);	
	//DEBUG_PRINT("kk_property_db_update_value sqlCmd:%s\n",sqlCmd);

	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   //DEBUG_PRINT("kk_property_db_update_value successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	return SUCCESS_RETURN;
}

int kk_property_db_get_value_directly(const char *deviceCode,const char *identifier,char* value)
{
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	sqlite3_stmt *stmt;
	char *valueStr = NULL;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();
	sqlCmd = sqlite3_mprintf("select * from PropertiesInfo WHERE deviceCode= '%s' and identifier = '%s'",deviceCode,identifier);	
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
        valueStr = sqlite3_column_text(stmt, DB_VALUE);
		printf("valueStr:%s\n",valueStr);
		memcpy(value,valueStr, strlen(valueStr));
		printf("value:%s\n",value);
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
    sqlite3_finalize(stmt);

	return SUCCESS_RETURN;
}

int kk_property_db_get_value(const char *deviceCode,const char *identifier,void* value)
{
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	sqlite3_stmt *stmt;
	char *valueStr = NULL;
	int valueType = 0;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();

	sqlCmd = sqlite3_mprintf("select * from PropertiesInfo WHERE deviceCode= '%s' and identifier = '%s'",deviceCode,identifier);	
	DEBUG_PRINT("kk_property_db_get_value sqlCmd:%s\n",sqlCmd);

	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	

	while(sqlite3_step(stmt) == SQLITE_ROW){
        valueStr = sqlite3_column_text(stmt, DB_VALUE);
        valueType = sqlite3_column_int(stmt, DB_VALUETYPE);
        if(valueType == KK_TSL_DATA_TYPE_INT||
            valueType == KK_TSL_DATA_TYPE_ENUM||
            valueType == KK_TSL_DATA_TYPE_BOOL){
            int value_int = atoi(valueStr);
            *(int*)value = value_int;

        }
		else if(valueType == KK_TSL_DATA_TYPE_FLOAT){
			float value_float = atoi(valueStr);
			*(float*)value = value_float;
		}
		else if(valueType == KK_TSL_DATA_TYPE_DOUBLE){
			double value_double = atoi(valueStr);
			*(double*)value = value_double;
		}
		else{
			memcpy(value,valueStr, strlen(valueStr));
		}
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
    sqlite3_finalize(stmt);

	return SUCCESS_RETURN;
}

int kk_property_db_get_rawdata(const char *identifier,const int dev_type, kk_prop_raw_struct_t* raw, int count)
{
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	sqlite3_stmt *stmt;
	char *valueStr = NULL;
	char *devcode = NULL;
	int valueType = 0;
	int idx = 0;
	kk_prop_raw_struct_t *curData = NULL;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();

	sqlCmd = sqlite3_mprintf("select * from PropertiesInfo WHERE devType= '%d' and identifier = '%s'",dev_type,identifier);	
	DEBUG_PRINT("kk_property_db_get_gw_value sqlCmd:%s\n",sqlCmd);

	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	
    curData = raw;
	while(sqlite3_step(stmt) == SQLITE_ROW && idx < count){
	    devcode = sqlite3_column_text(stmt, DB_DEVICECODE);
        valueStr = sqlite3_column_text(stmt, DB_VALUE);
        valueType = sqlite3_column_int(stmt, DB_VALUETYPE);
        memcpy(curData->deviceCode,devcode, strlen(devcode));
        memcpy(curData->raw,valueStr, strlen(valueStr));
        curData->type = valueType;
        
		curData++;
		idx++;
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
    sqlite3_finalize(stmt);

	return SUCCESS_RETURN;
}

int kk_property_db_update(const char *deviceCode)

{
    int res = 0;
	int idx = 0;
	int num = 0;
	char tmpValue[20] = {0};

    dm_mgr_dev_node_t *node = NULL;
	kk_tsl_data_t *property = NULL;	
    res = dm_mgr_get_device_by_devicecode(deviceCode, &node);
    if (res != SUCCESS_RETURN) {
        return FAIL_RETURN;
    }
	num = node->dev_shadow->property_number;
	for(idx = 0; idx < num; idx++){
		property = node->dev_shadow->properties + idx;
		if(property->data_value.type == KK_TSL_DATA_TYPE_INT ||
			property->data_value.type == KK_TSL_DATA_TYPE_ENUM||
			property->data_value.type == KK_TSL_DATA_TYPE_BOOL){
			sprintf(tmpValue,"%d",property->data_value.value_int);
			kk_property_db_update_value(deviceCode,property->identifier,tmpValue);
		}
		else if(property->data_value.type == KK_TSL_DATA_TYPE_FLOAT){
			sprintf(tmpValue,"%f",property->data_value.value_float);
			kk_property_db_update_value(deviceCode,property->identifier,tmpValue);

		
		}
		else if(property->data_value.type == KK_TSL_DATA_TYPE_DOUBLE){
			sprintf(tmpValue,"%f",property->data_value.value_double);
			kk_property_db_update_value(deviceCode,property->identifier,tmpValue);
		}	
		else if(property->data_value.type == KK_TSL_DATA_TYPE_TEXT||
			property->data_value.type == KK_TSL_DATA_TYPE_DATE){
			kk_property_db_update_value(deviceCode,property->identifier,property->data_value.value);
		
		}	
		else{
			ERROR_PRINT("Unkonwn Number Type");
		}	

	}
	return SUCCESS_RETURN;

}

int kk_property_sync_values(const char *deviceCode)
{
	char *sqlCmd = NULL;
	int rc = 0;
	int res = 0;
	char *zErrMsg = 0;
	sqlite3_stmt *stmt;	
	char *pIdentifier = NULL;
	char *valueStr = NULL;	
	int valueType = 0;

    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
	char *searchCmd = "select * from PropertiesInfo;";
	dm_mgr_dev_node_t *node = NULL;

	dm_mgr_get_device_by_devicecode(deviceCode,&node);

	sqlCmd = sqlite3_mprintf(searchCmd,deviceCode);	

	_kk_property_db_lock();
	sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
	

	while(sqlite3_step(stmt) == SQLITE_ROW){

		if(!strcmp(deviceCode,sqlite3_column_text(stmt, DB_DEVICECODE)))
		{
		   pIdentifier = sqlite3_column_text(stmt, DB_IDENTIFITER);
		   valueStr = sqlite3_column_text(stmt, DB_VALUE);
		   valueType = sqlite3_column_int(stmt, DB_VALUETYPE);
		   if(valueType == KK_TSL_DATA_TYPE_INT||
		   		valueType == KK_TSL_DATA_TYPE_ENUM||
		   		valueType == KK_TSL_DATA_TYPE_BOOL){
		   		int value_int = atoi(valueStr);
			    res = kk_tsl_set_value(kk_tsl_set_property_value,node->dev_shadow,pIdentifier,&value_int,NULL);
			}
			else if(valueType == KK_TSL_DATA_TYPE_FLOAT){
				float value_float = atof(valueStr);
				res = kk_tsl_set_value(kk_tsl_set_property_value,node->dev_shadow,pIdentifier,&value_float,NULL);
			}
			else if(valueType == KK_TSL_DATA_TYPE_DOUBLE){
				double value_double = atof(valueStr);
				res = kk_tsl_set_value(kk_tsl_set_property_value,node->dev_shadow,pIdentifier,&value_double,NULL);
			}
			else if(valueType == KK_TSL_DATA_TYPE_TEXT || valueType == KK_TSL_DATA_TYPE_DATE){
				res = kk_tsl_set_value(kk_tsl_set_property_value,node->dev_shadow,pIdentifier,NULL,valueStr);
			}
			if(res != SUCCESS_RETURN)
			{
				ERROR_PRINT("[%s][%d] res:%d\n",__FUNCTION__,__LINE__,res);
			}
		}


	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	sqlite3_finalize(stmt);

	return SUCCESS_RETURN;
}
int kk_property_delete_by_dcode(char deviceCode[DEVICE_CODE_MAXLEN])
{
	const char *deleteCmd = "delete from PropertiesInfo where deviceCode = '%s';";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,deviceCode);	

	INFO_PRINT("Table delete data sqlCmd:%s\n",sqlCmd);
	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   INFO_PRINT("Table delete data successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	return SUCCESS_RETURN;
}

int kk_property_delete_by_identify(char *identify)
{
	const char *deleteCmd = "delete from PropertiesInfo where identifier = '%s';";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,identify);	

	INFO_PRINT("Table delete data sqlCmd:%s\n",sqlCmd);
	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   INFO_PRINT("Table delete data successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	return SUCCESS_RETURN;
}

static int _kk_check_lockkey_exist(const char* deviceCode,const char* keyId)
{
	int isExist = 0;
	sqlite3_stmt *stmt;
	char *pDeviceCode = NULL;
	char *pKeyId = NULL;
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
	const char *searchCmd = "select * from LockKeysInfo;";	
	_kk_property_db_lock();
	
	sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
	//INFO_PRINT("total_column = %d\n", sqlite3_column_count(stmt));
	while(sqlite3_step(stmt) == SQLITE_ROW){
	   pDeviceCode = sqlite3_column_text(stmt, DB_LOCKKEY_DEVICECODE);

	   pKeyId = sqlite3_column_text(stmt, DB_LOCKKEY_KEYID);	   
	   if(!strcmp(deviceCode,pDeviceCode)&&!strcmp(pKeyId,keyId))
	   
{
		 isExist = 1;
		 break;
	   }
    }
    //INFO_PRINT("\n");
	sqlite3_finalize(stmt);
	_kk_property_db_unlock();
	return isExist;
}


int kk_property_update_lockkeys(char deviceCode[DEVICE_CODE_MAXLEN],char *keyId,int KeyType, int KeyRole, \
		int IsValid,char *KeyName,int KeyEffectiveTime,int KeyExpiryTime)
{
    kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
	char *zErrMsg = 0;
	int rc = 0;
	char *sqlCmd = NULL;
	if(_kk_check_lockkey_exist(deviceCode,keyId)){
		sqlCmd = sqlite3_mprintf("UPDATE LockKeysInfo SET KeyType='%d', IsValid='%d', KeyRole='%d', \
			KeyName='%s', KeyEffectiveTime='%d', KeyExpiryTime='%d' WHERE deviceCode= '%s' and KeyID = '%s'",KeyType,IsValid,KeyRole,KeyName,
					KeyEffectiveTime,KeyExpiryTime,deviceCode,keyId);	
	}
	else{
		const char *insertCmd = "insert into LockKeysInfo (deviceCode,KeyID,KeyType,KeyRole,IsValid,KeyName,KeyEffectiveTime,KeyExpiryTime) \
									values ('%s','%s','%d','%d','%d','%s','%d',%d);";
		sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,keyId,KeyType,KeyRole,IsValid,KeyName,KeyEffectiveTime,KeyExpiryTime);								

	}
	_kk_property_db_lock();
	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   //INFO_PRINT("sub device insert data successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();
	return SUCCESS_RETURN;

}

int kk_property_delete_lockkeys(char deviceCode[DEVICE_CODE_MAXLEN],char *keyId)
{
	const char *deleteCmd = "delete from LockKeysInfo where deviceCode = '%s' and KeyID = '%s';";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();

	_kk_property_db_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,deviceCode,keyId);	

	INFO_PRINT("Table delete data sqlCmd:%s\n",sqlCmd);
	rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( rc != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	}else{
	   INFO_PRINT("Table delete data successfully\n");
	}
	sqlite3_free(sqlCmd);
	_kk_property_db_unlock();

	return SUCCESS_RETURN;

}


