#include <stdio.h>
#include "kk_tsl_api.h"
#include "kk_dm_mng.h"
#include "sqlite3.h"
#include "kk_log.h"
#include "kk_scene_handle.h"
#include "cJSON.h"
#include <time.h>

extern sqlite3 *g_kk_pDb;
static void kk_scene_send_action_msg(kk_scene_action_info_t *pInfo);
static int kk_scene_update_starttime(kk_scene_timer_list_t *pInfo,int starttime,int current);

typedef struct {
    void *mutex;
    sqlite3 *pDb;
	void *s_scene_thread;

} kk_scene_ctx_t;


typedef enum{
	DB_SCENETYPE_SCENE = 0,
	DB_SCENETYPE_IFTT,
	DB_SCENETYPE_TIMING,
};

typedef enum{
	DB_SCENEINFO_NAME = 0,
	DB_SCENEINFO_SCENETYPE,
	DB_SCENEINFO_ENABLE,
	DB_SCENEINFO_SCENEID,
};
	
typedef enum{
	DB_SCENETRIGGER_TYPE = 0,
	DB_SCENETRIGGER_DEVICECODE,
	DB_SCENETRIGGER_EPNUM,
	DB_SCENETRIGGER_PROPERTYNAME,
	DB_SCENETRIGGER_COMPARETYPE,
	DB_SCENETRIGGER_COMPAREVALUE,
	DB_SCENETRIGGER_SCENEID,
};

typedef enum{
	DB_SCENECONDITION_TYPE = 0,
	DB_SCENECONDITION_STARTTIME,
	DB_SCENECONDITION_ENDTIME,
	DB_SCENECONDITION_CROSSDAY,
	DB_SCENECONDITION_REPEATDAY,
	DB_SCENECONDITION_SCENEID,
};

typedef enum{
	DB_SCENEACTION_TYPE = 0,
	DB_SCENEACTION_DEVICECODE,
	DB_SCENEACTION_EPNUM,	
	DB_SCENEACTION_PROPERTYNAME,
	DB_SCENEACTION_PROPERTYVALUE,
	DB_SCENEACTION_DELAY,
	DB_SCENEACTION_SCENEID,
};

static kk_scene_ctx_t s_kk_scene_ctx = {NULL};

static kk_scene_ctx_t *_kk_scene_get_ctx(void)
{
    return &s_kk_scene_ctx;
}
static void _kk_scene_lock(void)
{
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
    if (ctx->mutex) {
        HAL_MutexLock(ctx->mutex);
    }
}

static void _kk_scene_unlock(void)
{
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();

    if (ctx->mutex) {
        HAL_MutexUnlock(ctx->mutex);
    }
}

static int _kk_scene_db_init(void)
{
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
    char *pcErr;

    //eUtils_LockLock(&sLock);
    _kk_scene_lock();
    ctx->pDb = g_kk_pDb;
   
    INFO_PRINT("scene db Database opened\n");
    
	const char *pSceneTable = "CREATE TABLE IF NOT EXISTS SceneInfo( \
		name varchar(255), \
		sceneType INTEGER, \
		enable INTEGER, \
		sceneId varchar(255) UNIQUE)";

    if (sqlite3_exec(ctx->pDb, pSceneTable, NULL, NULL, &pcErr) != SQLITE_OK)
    {
        ERROR_PRINT("Error creating table (%s)\n", pcErr);
        sqlite3_free(pcErr);
        //eUtils_LockUnlock(&sLock);
        _kk_scene_unlock();
        return FAIL_RETURN;
    }

	const char *pSceneTriggerTable = "CREATE TABLE IF NOT EXISTS SceneTriggerInfo( \
		type varchar(255), \
		deviceCode varchar(255), \
		epNum INTEGER, \
		propertyName varchar(255), \
		compareType varchar(255), \
		compareValue varchar(255), \
		sceneId varchar(255))";


    if (sqlite3_exec(ctx->pDb, pSceneTriggerTable, NULL, NULL, &pcErr) != SQLITE_OK)
    {
        ERROR_PRINT("Error creating table (%s)\n", pcErr);
        sqlite3_free(pcErr);
        //eUtils_LockUnlock(&sLock);
        _kk_scene_unlock();
        return FAIL_RETURN;
    }

	const char *pSceneConditionTable = "CREATE TABLE IF NOT EXISTS SceneConditionInfo( \
		type varchar(255), \
		startTime INTEGER, \
		endTime INTEGER, \
		crossDay INTEGER, \
		repeat_days INTEGER, \
		sceneId varchar(255))";


    if (sqlite3_exec(ctx->pDb, pSceneConditionTable, NULL, NULL, &pcErr) != SQLITE_OK)
    {
        ERROR_PRINT("Error creating table (%s)\n", pcErr);
        sqlite3_free(pcErr);
        //eUtils_LockUnlock(&sLock);
        _kk_scene_unlock();
        return FAIL_RETURN;
    }	

	const char *pSceneActionTable = "CREATE TABLE IF NOT EXISTS SceneActionInfo( \
		type varchar(255), \
		deviceCode varchar(255), \
		epNum INTEGER, \
		propertyName varchar(255), \
		propertyValue varchar(255), \
		delay INTEGER, \
		sceneId varchar(255))";


    if (sqlite3_exec(ctx->pDb, pSceneActionTable, NULL, NULL, &pcErr) != SQLITE_OK)
    {
        ERROR_PRINT("Error creating table (%s)\n", pcErr);
        sqlite3_free(pcErr);
        //eUtils_LockUnlock(&sLock);
        _kk_scene_unlock();
        return FAIL_RETURN;
    }	

    _kk_scene_unlock();
    return SUCCESS_RETURN;
}

#define KK_DEVICE_TSL_TYPE (0x08)
static kk_tsl_t *s_scene_shadow = NULL;
static int kk_scene_tsl_load(void)
{
	int res = 0;
	char *tsl_str = NULL;
	int heartTime = 0;
	tsl_str = kk_load_json("15", KK_DEVICE_TSL_TYPE);
	if(tsl_str != NULL)
	{
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		res = kk_tsl_create(tsl_str,strlen(tsl_str),&s_scene_shadow,&heartTime);
		free(tsl_str);
		if(res != 0){
			return FAIL_RETURN;
		}
	}
	printf("[%s][%d]\n",__FUNCTION__,__LINE__);
	return SUCCESS_RETURN;

}
static int kk_scene_check_able(char *sceneId)
{
	int res = 0;
	int sceneType = 0;
	int enable = 0;
	if(sceneId == NULL){
		return INVALID_PARAMETER;
	}
	res = kk_scene_get_scene_info(sceneId,&sceneType,&enable);
	if(res == SUCCESS_RETURN&&sceneType == DB_SCENETYPE_TIMING && enable == 1){
		return SUCCESS_RETURN;
	}
	return FAIL_RETURN;
}
static int kk_scene_timer_load(void)
{
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	sqlite3_stmt *stmt;
	char *sceneId = NULL;
	char *sqlCmd = "select * from SceneConditionInfo WHERE type = 'timeRange'";
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);

	while(sqlite3_step(stmt) == SQLITE_ROW){
	    sceneId = sqlite3_column_text(stmt, DB_SCENECONDITION_SCENEID);
		if(kk_scene_check_able(sceneId) == SUCCESS_RETURN){

			int starttime = sqlite3_column_int(stmt, DB_SCENECONDITION_STARTTIME);
			int endtime = sqlite3_column_int(stmt, DB_SCENECONDITION_ENDTIME);
			int repeatday = sqlite3_column_int(stmt, DB_SCENECONDITION_REPEATDAY);
			kk_scene_push_timer_info(starttime,endtime,repeatday,sceneId);
		}
	}
	sqlite3_finalize(stmt);
	return SUCCESS_RETURN;

}
kk_tsl_t * kk_scene_shadow(void)
{
	return s_scene_shadow;
}
static kk_scene_action_delay_t *p_delay_action_list = NULL;
static kk_scene_timer_list_t *p_scene_timer_list = NULL;
extern uint64_t s_start_time;

void *kk_scene_yield(void *args)
{
	uint64_t current_time = 0;
	kk_scene_action_delay_t *actionDelayInfo = NULL;
	kk_scene_action_delay_t *pTemp = NULL;	
	kk_scene_timer_list_t *scene_timer_list = NULL;
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
    while (1) {
		current_time = HAL_GetTime();
		/****系统起来15s后开始定时处理****/
		if((HAL_UptimeMs() - s_start_time) <= 15000){
			printf("[%s][%d]\n",__FUNCTION__,__LINE__);
			sleep(1);
			continue;
		}
		
		_kk_scene_lock();
		actionDelayInfo = p_delay_action_list;
		while(actionDelayInfo){	
			INFO_PRINT("[%s][%d] current_time:%d\n",__FUNCTION__,__LINE__,current_time);
			INFO_PRINT("[%s][%d] actionDelayInfo->starttime:%d\n",__FUNCTION__,__LINE__,actionDelayInfo->starttime);
			if(current_time >= actionDelayInfo->starttime){
				INFO_PRINT("[%s][%d]\n",__FUNCTION__,__LINE__);
				kk_scene_send_action_msg(actionDelayInfo->action);
				if(actionDelayInfo == p_delay_action_list){
					pTemp = p_delay_action_list;
					p_delay_action_list = p_delay_action_list->next;
					free(pTemp);
					actionDelayInfo = p_delay_action_list;
				}
				else{
					pTemp->next = actionDelayInfo->next;
					free(actionDelayInfo);
					actionDelayInfo = pTemp->next;
				}
			}
			else{
				pTemp= actionDelayInfo;
				actionDelayInfo = actionDelayInfo->next;
			}
		}
		_kk_scene_unlock();

		_kk_scene_lock();
		/*******定时模式**********/
		scene_timer_list = p_scene_timer_list;
		while(scene_timer_list){	
			INFO_PRINT("--------scene_timer_list->starttime:%d\n",scene_timer_list->starttime);
			INFO_PRINT("--------current_time %d\n",current_time);
			INFO_PRINT("--------scene_timer_list->end:%d\n",scene_timer_list->endtime);
			if(current_time >= scene_timer_list->starttime && \
				current_time <= scene_timer_list->endtime){
				kk_scene_execute_action(scene_timer_list->sceneId);
				kk_scene_update_starttime(scene_timer_list,scene_timer_list->starttime,current_time);
			}
			scene_timer_list = scene_timer_list->next;
		}
		_kk_scene_unlock();
		sleep(1);
    }

    return NULL;
}

int kk_scene_init(void)
{
	int res = 0;
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();

    /* Create Mutex */
    ctx->mutex = HAL_MutexCreate();
    if (ctx->mutex == NULL) {
        return FAIL_RETURN;
    }

	_kk_scene_db_init();

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

	kk_scene_timer_load();

    res = pthread_create(&ctx->s_scene_thread, NULL, kk_scene_yield, NULL);
    if (res < 0) {
        ERROR_PRINT("HAL_ThreadCreate mid Failed\n");
        //IOT_Linkkit_Close(mid_ctx->master_devid);
        return -1;
    }	

	return SUCCESS_RETURN;
}

int kk_scene_update_scene_enable(int enable,const char *sceneId)
{
	char *sqlCmd = NULL;
	int len =0;
	int rc = 0;
	char *zErrMsg = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();

	//_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf("UPDATE SceneInfo SET enable=%d  WHERE sceneId= '%s'",enable,sceneId);	
	INFO_PRINT("kk_scene_update_scene_enable,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 updata data successfully\n");
	}
    sqlite3_free(sqlCmd);
	//_kk_subDb_unlock();
	return SUCCESS_RETURN;
}

static int kk_scene_insert_scene_info(const char* name,const char* sceneType,const char* enable,const char* sceneId)
{
	int res = 0;
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;

	const char *insertCmd = "insert into SceneInfo (name, sceneType,enable,sceneId) \
								values ('%s','%d','%d','%s');";	
								
	_kk_scene_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,name,sceneType,enable,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;
	}
	
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	return SUCCESS_RETURN;

}
static int kk_scene_insert_scene_trigger(const char* type,const char* deviceCode,const char* epNum,const char* propertyName,
												const char* compareType,const char* compareValue,const char* sceneId)
{
	int res = 0;
    kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;

	const char *insertCmd = "insert into SceneTriggerInfo (type, deviceCode,epNum,propertyName,compareType,compareValue,sceneId) \
								values ('%s','%s','%d','%s','%s','%s','%s');";	
								
	_kk_scene_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,type,deviceCode,epNum,propertyName,compareType,compareValue,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;	
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	
	return SUCCESS_RETURN;

}

static int kk_scene_insert_scene_condition(const char* type,int startTime,int endTime,int crossDay,
												char repeat_days,const char* sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;

	const char *insertCmd = "insert into SceneConditionInfo (type, startTime,endTime,crossDay,repeat_days,sceneId) \
								values ('%s','%d','%d','%d','%d','%s');";	
								
	_kk_scene_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,type,startTime,endTime,crossDay,repeat_days,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;	
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	
	return SUCCESS_RETURN;

}


static int kk_scene_insert_scene_action(const char* type,const char* deviceCode,int epNum,const char* propertyName,
												const char* propertyValue,int delay,const char* sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;

	const char *insertCmd = "insert into SceneActionInfo (type, deviceCode,epNum,propertyName,propertyValue,delay,sceneId) \
								values ('%s','%s','%d','%s','%s','%d','%s');";	
								
	_kk_scene_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,type,deviceCode,epNum,propertyName,propertyValue,delay,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;	
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	
	return SUCCESS_RETURN;

}
int kk_scene_delete_scene_info(const char *sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;
	const char *deleteCmd = "delete from SceneInfo where sceneId = '%s';";
						
	_kk_scene_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;		
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	return SUCCESS_RETURN;
}
int kk_scene_delete_scene_trigger(const char *sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;
	const char *deleteCmd = "delete from SceneTriggerInfo where sceneId = '%s';";
						
	_kk_scene_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;		
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	return SUCCESS_RETURN;
}
int kk_scene_delete_scene_condition(const char *sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;
	const char *deleteCmd = "delete from SceneConditionInfo where sceneId = '%s';";
						
	_kk_scene_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;		
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	return SUCCESS_RETURN;
}

int kk_scene_delete_scene_action(const char *sceneId)
{
	int res = 0;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	char *sqlCmd = NULL;
	char *zErrMsg = 0;
	const char *deleteCmd = "delete from SceneActionInfo where sceneId = '%s';";
						
	_kk_scene_lock();
	 sqlCmd = sqlite3_mprintf(deleteCmd,sceneId);	
	res = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
	if( res != SQLITE_OK ){
	   ERROR_PRINT("SQL error: %s\n", zErrMsg);
	   sqlite3_free(zErrMsg);
	   sqlite3_free(sqlCmd);
	   _kk_scene_unlock();	
	   return FAIL_RETURN;		
	}
	sqlite3_free(sqlCmd);
	_kk_scene_unlock();
	return SUCCESS_RETURN;
}


int kk_scene_parse_scene_trigger(const cJSON* str,const char *sceneId)
{
	int res = 0;
	if(str == NULL || sceneId == NULL){
		return INVALID_PARAMETER;
	}

	cJSON *triggers = cJSON_GetObjectItem(str,MSG_SCENE_TRIGGERS);
	if(triggers == NULL) return FAIL_RETURN;	
	cJSON *items = cJSON_GetObjectItem(triggers,MSG_SCENE_ITEMS);
	if(items == NULL) return FAIL_RETURN;
	cJSON * item = items->child;
	while(item != NULL){
		cJSON *type = cJSON_GetObjectItem(item,MSG_SCENE_TYPE);
		if(type == NULL) return FAIL_RETURN;
		cJSON *deviceCode = cJSON_GetObjectItem(item,MSG_DEVICE_CODE_STR);
		if(deviceCode == NULL) return FAIL_RETURN;	
		cJSON *epNum = cJSON_GetObjectItem(item,MSG_SCENE_EPNUM);
		if(epNum == NULL) return FAIL_RETURN;		
		cJSON *propertyName = cJSON_GetObjectItem(item,MSG_SCENE_PROPERTYNAME);
		if(propertyName == NULL) return FAIL_RETURN;
		cJSON *compareType = cJSON_GetObjectItem(item,MSG_SCENE_COMPARETYPE);
		if(compareType == NULL) return FAIL_RETURN;		
		cJSON *compareValue = cJSON_GetObjectItem(item,MSG_SCENE_COMPAREVALUE);
		if(compareValue == NULL) return FAIL_RETURN;	
		res = kk_scene_insert_scene_trigger(type->valuestring,deviceCode->valuestring,deviceCode->valueint,
				propertyName->valuestring,compareType->valuestring,compareValue->valuestring,sceneId);
		if(res != SUCCESS_RETURN){
			ERROR_PRINT("kk_scene_parse_scene_trigger fail!!!\n");
			return res;
		}
		item = item->next;	
	}
	INFO_PRINT("kk_scene_parse_scene_trigger success!!!\n");
	return SUCCESS_RETURN;
}
static int kk_scene_parse_repeatday(cJSON *repeatday)
{
	/***weekflag 01111111  ---> week 7 6 5 4 3 2 1*/
	char weekflag = 0;
	int iCnt = 0;
	if(repeatday == NULL){
		return INVALID_PARAMETER;
	}
	int  array_size = cJSON_GetArraySize (repeatday);
	INFO_PRINT("array_size:%d\n",array_size);
	if(array_size == 0){
		weekflag = 0;
	}else{
		for(iCnt = 0; iCnt < array_size; iCnt++){
			cJSON * pSub = cJSON_GetArrayItem(repeatday, iCnt);
			if(pSub != NULL){
				weekflag = weekflag|(1<<(pSub->valueint-1));
			}
		}
	}
	INFO_PRINT("kk_scene_parse_repeatday weekflag:%d!!!\n",weekflag);
	return weekflag;
	
}
void kk_scene_remove_timer_info(char *sceneId)
{
	kk_scene_timer_list_t *ptr = NULL,*ptemp = NULL;
	_kk_scene_lock();
	ptemp = ptr = p_scene_timer_list;
	while(ptr){ 		
		if(!strcmp(ptr->sceneId,sceneId)){
			if(ptr == p_scene_timer_list){
				ptemp = p_scene_timer_list;
				p_scene_timer_list = p_scene_timer_list->next;
				free(ptemp);
				ptr = p_scene_timer_list;
			}
			else{
				ptemp->next = ptr->next;
				free(ptr);
				ptr = ptemp->next;
			}
		}
		else{
			ptemp= ptr;
			ptr = ptr->next;
		}
	}
	_kk_scene_unlock();

}
int kk_scene_push_timer_info(int starttime,int endtime,int repeatday,char *sceneId)
{
	kk_scene_timer_list_t *ptr = NULL,*ptemp = NULL;
	if(sceneId == NULL){
		return INVALID_PARAMETER;
	}
	_kk_scene_lock();
	ptemp = ptr = p_scene_timer_list;
	while(ptr){
		ptemp = ptr;
		ptr = ptr->next;
	}		
	ptr = malloc(sizeof(kk_scene_timer_list_t));
	if(ptr == NULL) {
		_kk_scene_unlock();		
		return MEMORY_NOT_ENOUGH;
	}
	memset(ptr,0x0,sizeof(kk_scene_timer_list_t));
	ptr->starttime = starttime;
	ptr->endtime = endtime;
	ptr->repeatday = repeatday;
	memcpy(ptr->sceneId,sceneId,strlen(sceneId));
	if(p_scene_timer_list == NULL){
		p_scene_timer_list = ptr;
	}else{
		ptemp->next = ptr;
	}
	_kk_scene_unlock();
	INFO_PRINT("kk_scene_push_timer_info called!!!\n");
	return SUCCESS_RETURN;
}

int kk_scene_parse_scene_condition(const cJSON* str,const char *sceneId)
{
	char weekflag = 0;
	int res = 0;
	if(str == NULL || sceneId == NULL){
		ERROR_PRINT("kk_scene_parse_scene_condition failed\n");
		return INVALID_PARAMETER;
	}

	cJSON *conditon = cJSON_GetObjectItem(str,MSG_SCENE_CONDITIONS);
	if(conditon == NULL) return FAIL_RETURN;	

	cJSON *items = cJSON_GetObjectItem(conditon,MSG_SCENE_ITEMS);
	if(items == NULL) return FAIL_RETURN;
	cJSON * item = items->child;
	while(item != NULL){
		cJSON *type = cJSON_GetObjectItem(item,MSG_SCENE_TYPE);
		if(type == NULL) return FAIL_RETURN;

		cJSON *startTime = cJSON_GetObjectItem(item,MSG_SCENE_STARTTIME);
		if(startTime == NULL) return FAIL_RETURN;
		cJSON *endTime = cJSON_GetObjectItem(item,MSG_SCENE_ENDTIME);
		if(endTime == NULL) return FAIL_RETURN;	
		cJSON *crossday = cJSON_GetObjectItem(item,MSG_SCENE_CROSSDAY);
		if(crossday == NULL) return FAIL_RETURN;	
		cJSON *repeatday = cJSON_GetObjectItem(item,MSG_SCENE_REPEATDAYS);
		if(repeatday == NULL) return FAIL_RETURN;
		weekflag = kk_scene_parse_repeatday(repeatday);
		if(!strcmp("timeRange",type->valuestring)){
			kk_scene_push_timer_info(startTime->valueint,endTime->valueint,weekflag,sceneId);
		}		
		res = kk_scene_insert_scene_condition(type->valuestring,startTime->valueint,endTime->valueint,
				crossday->valueint,weekflag,sceneId);
		if(res != SUCCESS_RETURN){
			INFO_PRINT("kk_scene_insert_scene_condition fail!!!\n");
			return res;
		}
		item = item->next;
	}
	INFO_PRINT("kk_scene_parse_scene_condition success!!!\n");
	return SUCCESS_RETURN;
}

int kk_scene_parse_scene_action(const cJSON* str,const char *sceneId)
{
	int res = 0;
	if(str == NULL || sceneId == NULL){
		ERROR_PRINT("kk_scene_parse_scene_action failed\n");
		return INVALID_PARAMETER;
	}

	cJSON *action = cJSON_GetObjectItem(str,MSG_SCENE_ACTIONS);
	if(action == NULL) return FAIL_RETURN;
	
	//cJSON *items = cJSON_GetObjectItem(conditon,MSG_SCENE_ITEMS);
	//if(items == NULL) return FAIL_RETURN;
	cJSON * item = action->child;
	while(item != NULL){
		cJSON *type = cJSON_GetObjectItem(item,MSG_SCENE_TYPE);
		if(type == NULL) return FAIL_RETURN;	
		cJSON *deviceCode = cJSON_GetObjectItem(item,MSG_DEVICE_CODE_STR);
		if(deviceCode == NULL) return FAIL_RETURN;	
		cJSON *epNum = cJSON_GetObjectItem(item,MSG_SCENE_EPNUM);
		if(epNum == NULL) return FAIL_RETURN;	
		cJSON *propertyName = cJSON_GetObjectItem(item,MSG_SCENE_PROPERTYNAME);
		if(propertyName == NULL) return FAIL_RETURN;
		cJSON *propertyValue = cJSON_GetObjectItem(item,MSG_SCENE_PROPERTYVALUE);
		if(propertyValue == NULL) return FAIL_RETURN;
		cJSON *delay = cJSON_GetObjectItem(item,MSG_SCENE_DELAY);
		if(delay == NULL) return FAIL_RETURN;

		res = kk_scene_insert_scene_action(type->valuestring,deviceCode->valuestring,epNum->valueint,
				propertyName->valuestring,propertyValue->valuestring,delay->valueint,sceneId);
		if(res != SUCCESS_RETURN){
			INFO_PRINT("kk_scene_insert_scene_action fail!!!\n");
			return res;
		}		
		item = item->next;
	}
	INFO_PRINT("kk_scene_parse_scene_action success!!!\n");
	return SUCCESS_RETURN;
}


int kk_scene_parse_addscene(const cJSON* args,char *sceneId,int isUpdate)
{
	int res = 0;

	if(args == NULL||sceneId == NULL){
		ERROR_PRINT("[%d]kk_scene_parse_addscene fail!!!\n",__LINE__);
		return INVALID_PARAMETER;
	}
	
	cJSON *name = cJSON_GetObjectItem(args,MSG_SCENE_NAME);
	if(name == NULL) return FAIL_RETURN;
	cJSON *sceneType = cJSON_GetObjectItem(args,MSG_SCENE_SCENCTYPE);
	if(sceneType == NULL) return FAIL_RETURN;
	cJSON *enable = cJSON_GetObjectItem(args,MSG_SCENE_ENABLE);
	if(enable == NULL) return FAIL_RETURN;	
	if(!isUpdate){
		HAL_GetTimeMs(sceneId);//use time to create the sceneId
	}
	//sprintf(sceneId,"%lld",u64SceneKeyId);
	
	res = kk_scene_insert_scene_info(name->valuestring,sceneType->valueint,enable->valueint,sceneId);
	if(res != SUCCESS_RETURN){
		INFO_PRINT("kk_scene_insert_scene_info fail!!!\n");
		return res;
	}	

	res = kk_scene_parse_scene_trigger(args,sceneId);
	if(res != SUCCESS_RETURN){
		ERROR_PRINT("kk_scene_parse_scene_trigger failed\n");
		return FAIL_RETURN;
	}
	res = kk_scene_parse_scene_condition(args,sceneId);
	if(res != SUCCESS_RETURN){
		ERROR_PRINT("kk_scene_parse_scene_condition failed\n");
		return FAIL_RETURN;
	}	

	res = kk_scene_parse_scene_action(args,sceneId);
	if(res != SUCCESS_RETURN){
		ERROR_PRINT("kk_scene_parse_scene_action failed\n");
		return FAIL_RETURN;
	}		
	return SUCCESS_RETURN;

}
static void kk_scene_delete(char* sceneId)
{
	kk_scene_remove_timer_info(sceneId);
	kk_scene_delete_scene_info(sceneId);
	kk_scene_delete_scene_trigger(sceneId);
	kk_scene_delete_scene_condition(sceneId);	
	kk_scene_delete_scene_action(sceneId);
}
int kk_scene_parse_updatescene(const cJSON* arg,char *sceneId)
{

	if(arg == NULL||sceneId == NULL){
		ERROR_PRINT("[%d]kk_scene_parse_addscene fail!!!\n",__LINE__);
		return INVALID_PARAMETER;
	}
	kk_scene_delete(sceneId);
	return kk_scene_parse_addscene(arg,sceneId,1);
}
int kk_scene_parse_deletescene(char *sceneId)
{

	if(sceneId == NULL){
		ERROR_PRINT("[%d]kk_scene_parse_deletescene fail!!!\n",__LINE__);
		return INVALID_PARAMETER;
	}
	kk_scene_delete(sceneId);	
	return SUCCESS_RETURN;
}

int kk_scene_get_scene_info(const char* sceneId,int *sceneType,int *enable)
{	
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	sqlite3_stmt *stmt;
	int res = FAIL_RETURN;
	char *sqlCmd = NULL;
	sqlCmd = sqlite3_mprintf("select * from SceneInfo WHERE sceneId = '%s'",sceneId);	
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	    *sceneType = sqlite3_column_int(stmt, DB_SCENEINFO_SCENETYPE);
		*enable = sqlite3_column_int(stmt, DB_SCENEINFO_ENABLE);
		res =  SUCCESS_RETURN;
	}
	sqlite3_finalize(stmt);
	return res;
}
static char kk_scene_date_to_week(time_t t)
{
	struct tm *p = localtime(&t);  
    return ((p->tm_wday == 0)?7 : p->tm_wday);
}
static int kk_scene_update_starttime(kk_scene_timer_list_t *pInfo,int starttime,int current)
{
	char curWeek = 0;
	char i = 0;
	if(pInfo == NULL){
		return FAIL_RETURN;
	}
	/*only support once*/
	if(pInfo->repeatday == 0){
		pInfo->starttime = 0;
		pInfo->endtime = 0;	
		kk_scene_update_scene_enable(0,pInfo->sceneId);
		return SUCCESS_RETURN;
	}else{
		curWeek = kk_scene_date_to_week(current);
		for(i = 1; i < 8; i++){
			if(curWeek == 0x40){ //0x40 ==>01000000 ,sunday
				curWeek = curWeek>>6;
			}
			else{
				curWeek = curWeek<<1;
			}
			if(curWeek & pInfo->repeatday){
				pInfo->starttime = pInfo->starttime + 86400*i;
				pInfo->endtime = pInfo->endtime + 86400*i;
				return SUCCESS_RETURN;
			}			
		}
	
	}
	return FAIL_RETURN;

}

static int kk_scene_creat_new_starttime(time_t starttime,time_t current)
{
	struct tm *s = localtime(&starttime);  
	struct tm *c = localtime(&current); 
	c->tm_hour = s->tm_hour;
	c->tm_min = s->tm_min;
	c->tm_sec = s->tm_sec;
    return mktime(c);
}

int kk_scene_check_condition(const char *sceneId)
{
	int res = FAIL_RETURN;
	char *sqlCmd = NULL;
	char curWeek = 0;
	char repeatday = 0;
	int startTime = 0,startTime_m = 0;
	int endTime = 0,endTime_m = 0;
	int duration = 0;
	sqlite3_stmt *stmt;	
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	uint64_t current = HAL_GetTime();
	curWeek = kk_scene_date_to_week(current);

	sqlCmd = sqlite3_mprintf("select * from SceneConditionInfo WHERE sceneId = '%s'",sceneId);
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	    repeatday = sqlite3_column_int(stmt, DB_SCENECONDITION_REPEATDAY);
		startTime = sqlite3_column_int(stmt, DB_SCENECONDITION_STARTTIME);
		endTime = sqlite3_column_int(stmt, DB_SCENECONDITION_ENDTIME);
		duration = endTime - startTime;
		startTime_m = kk_scene_creat_new_starttime(startTime,current);
		/********check today is one of repeatday**************/
		if((repeatday > 0) && (repeatday &(1<<(curWeek-1)))){	
			if(current >= startTime_m && current <= (startTime_m + duration)){
				res =  SUCCESS_RETURN;
			}
		}
		else{
			if(current >= startTime && current <= (startTime + duration)){
				res =  SUCCESS_RETURN;
			}
		}
	}
	sqlite3_finalize(stmt);
	return res;

}
static int kk_scene_check_value(const char * compareType,const char * compareValue,cJSON *item,int valueType)
{
	int res = FAIL_RETURN;
	int type = 0;
	int ivalue = 0;
	int itemValue = 0;
	double dvalue = 0;
	double ditemValue = 0;

	if(compareType == NULL || compareValue == NULL ||item == NULL){
		ERROR_PRINT("[%d]kk_scene_check_value fail!!!\n",__LINE__);
		return INVALID_PARAMETER;
	}
	switch(valueType){
		case KK_TSL_DATA_TYPE_INT:
		case KK_TSL_DATA_TYPE_ENUM:
		case KK_TSL_DATA_TYPE_BOOL:
			ivalue = atoi(compareValue);
		    itemValue = item->valueint;
			break;
		case KK_TSL_DATA_TYPE_FLOAT:
		case KK_TSL_DATA_TYPE_DOUBLE:
			dvalue = atof(compareValue);
			ditemValue = item->valuedouble;
			type = 1;
			break;	
		case KK_TSL_DATA_TYPE_TEXT:
		case KK_TSL_DATA_TYPE_DATE:
			type = 2;
			break;
	}
	if(type == 2){
		if(!strcmp(compareType,"=") && !strcmp(compareValue,item->valuestring)){
			res = 0;
		}
		else if(!strcmp(compareType,">") && strcmp(item->valuestring,compareValue) > 0){
			res = 0;
		}
		else if(!strcmp(compareType,">=") && strcmp(item->valuestring,compareValue) >= 0){
			res = 0;
		}	
		else if(!strcmp(compareType,"<=") && strcmp(item->valuestring,compareValue) <= 0){
			res = 0;
		}	
		else if(!strcmp(compareType,"<") && strcmp(item->valuestring,compareValue) < 0){
			res = 0;
		}		
	}
	else if(type == 1){
		if(!strcmp(compareType,"=")){
			if(ditemValue == dvalue) 
				res = 0;
		}
		else if(!strcmp(compareType,">")){
			if(ditemValue > dvalue) 
				res = 0;
		}
		else if(!strcmp(compareType,">=")){
			if(ditemValue >= dvalue) 
				res = 0;
		}
		else if(!strcmp(compareType,"<=")){
			if(ditemValue <= dvalue) 
				res = 0;
		}
		else if(!strcmp(compareType,"<")){
			if(ditemValue < dvalue) 
				res = 0;
		}	
	}
	else{
		if(!strcmp(compareType,"=")){
			if(itemValue == ivalue) 
				res = 0;
		}
		else if(!strcmp(compareType,">")){
			if(itemValue > ivalue) 
				res = 0;
		}
		else if(!strcmp(compareType,">=")){
			if(itemValue >= ivalue) 
				res = 0;
		}
		else if(!strcmp(compareType,"<=")){
			if(itemValue <= ivalue) 
				res = 0;
		}
		else if(!strcmp(compareType,"<")){
			if(itemValue < ivalue) 
				res = 0;
		}	

	}
	return res;

}
static void kk_scene_send_action_msg(kk_scene_action_info_t *pInfo)
{
	int ivalue = 0;
	double dvalue = 0; 
	int valueType = 0;
	if(pInfo == NULL){
		return;
	}
	cJSON *root=cJSON_CreateObject();	

	valueType = kk_dm_get_property_type(pInfo->deviceCode,pInfo->propertyName);
	if(valueType < 0){
		ERROR_PRINT("[%d]kk_scene_send_action_msg valueType < 0!!!\n",__LINE__);
		return 0;
	}
	switch(valueType){
		case KK_TSL_DATA_TYPE_INT:
		case KK_TSL_DATA_TYPE_ENUM:
		case KK_TSL_DATA_TYPE_BOOL:
			ivalue = atoi(pInfo->propertyValue);
			cJSON_AddNumberToObject(root,pInfo->propertyName,ivalue);
			break;
		case KK_TSL_DATA_TYPE_FLOAT:
		case KK_TSL_DATA_TYPE_DOUBLE:
			dvalue = atof(pInfo->propertyValue);
			cJSON_AddNumberToObject(root,pInfo->propertyName,dvalue);
			break;
		case KK_TSL_DATA_TYPE_TEXT:
		case KK_TSL_DATA_TYPE_DATE:
			cJSON_AddStringToObject(root,pInfo->propertyName,pInfo->propertyValue);
			break;
	}
	
	char *out=cJSON_Print(root);
	kk_msg_execute_property_set(pInfo->productCode,pInfo->deviceCode,out,pInfo->fatherdeviceCode);
	cJSON_Delete(root); 
	free(out);
	free(pInfo);
	return;
}

static int kk_scene_start_action(const char *deviceCode,const char *propertyName,const char *valueS,int delay)
{
	int res = 0;
	
	INFO_PRINT("[%s][%d]kk_scene_start_action called\n",__FUNCTION__,__LINE__);
	dm_mgr_dev_node_t *node = NULL;
	kk_scene_action_info_t *actionInfo = NULL;
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	
	if(deviceCode == NULL || propertyName == NULL || valueS == NULL){
		ERROR_PRINT("[%d]kk_scene_send_action fail!!!\n",__LINE__);
		return INVALID_PARAMETER;
	}
	res = dm_mgr_get_device_by_devicecode(deviceCode, &node);
    if (res != SUCCESS_RETURN) {
        return FAIL_RETURN;
    }
	actionInfo = malloc(sizeof(kk_scene_action_info_t));
	if(actionInfo == NULL) {
		return MEMORY_NOT_ENOUGH;	
	}
	memset(actionInfo,0x0,sizeof(kk_scene_action_info_t));
	memcpy(actionInfo->deviceCode,deviceCode,strlen(deviceCode));
	memcpy(actionInfo->productCode,node->productCode,strlen(node->productCode));
	memcpy(actionInfo->fatherdeviceCode,node->fatherDeviceCode,strlen(node->fatherDeviceCode));
	memcpy(actionInfo->propertyName,propertyName,strlen(propertyName));	
	memcpy(actionInfo->propertyValue,valueS,strlen(valueS));		
	if(delay == 0){
		INFO_PRINT("[%s][%d]\n",__FUNCTION__,__LINE__);
		kk_scene_send_action_msg(actionInfo);
	}else{
		INFO_PRINT("[%s][%d]delay:%d\n",__FUNCTION__,__LINE__,delay);
		kk_scene_action_delay_t *ptr = NULL,*ptemp = NULL;
		ptemp = ptr = p_delay_action_list;
		while(ptr){
			ptemp = ptr;
			ptr = ptr->next;
		}		
		ptr = malloc(sizeof(kk_scene_action_delay_t));
		if(ptr == NULL) {
			return MEMORY_NOT_ENOUGH;
		}
		memset(ptr,0x0,sizeof(kk_scene_action_delay_t));
		ptr->starttime = HAL_GetTime()+delay;
		ptr->action = actionInfo;
		if(p_delay_action_list == NULL){
			p_delay_action_list = ptr;
		}else{
			ptemp->next = ptr;
		}
	}
	return SUCCESS_RETURN;


}
int kk_scene_execute_action(const char* sceneId)
{
	int res = FAIL_RETURN;
	char *deviceCode = NULL;
	char *propertyName = NULL;
	char *propertyValue = NULL;	
	int delay = 0;
	char *sqlCmd = NULL;
	sqlite3_stmt *stmt;	
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();
	sqlCmd = sqlite3_mprintf("select * from SceneActionInfo WHERE sceneId = '%s'",sceneId);
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	    deviceCode = sqlite3_column_text(stmt, DB_SCENEACTION_DEVICECODE);
		propertyName = sqlite3_column_text(stmt,DB_SCENEACTION_PROPERTYNAME);
		propertyValue = sqlite3_column_text(stmt,DB_SCENEACTION_PROPERTYVALUE);
		delay = sqlite3_column_int(stmt,DB_SCENEACTION_DELAY);
		res = kk_scene_start_action(deviceCode,propertyName,propertyValue,delay);
	}
	sqlite3_finalize(stmt);
	return res;

}

int kk_scene_query_trigger_info(const char *deviceCode,cJSON *param)
{
	int res = FAIL_RETURN;
	char *sqlCmd = NULL;
	char *zErrMsg = 0;
	char *sceneId = NULL;
	char *identifier = NULL;
	char *compareType = NULL;	
	char *compareValue = NULL;	
	int propertyValueType = 0;
	int sceneType = 0,isEnable = 0;
	sqlite3_stmt *stmt;	
	kk_scene_ctx_t *ctx = _kk_scene_get_ctx();

	sqlCmd = sqlite3_mprintf("select * from SceneTriggerInfo WHERE deviceCode= '%s'",deviceCode);	
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	    sceneId = sqlite3_column_text(stmt, DB_SCENETRIGGER_SCENEID);
		identifier = sqlite3_column_text(stmt,DB_SCENETRIGGER_PROPERTYNAME);
		compareType = sqlite3_column_text(stmt,DB_SCENETRIGGER_COMPARETYPE);
		compareValue = sqlite3_column_text(stmt,DB_SCENETRIGGER_COMPAREVALUE);
		res = kk_scene_get_scene_info(sceneId,&sceneType,&isEnable);
		if(res == SUCCESS_RETURN && sceneType == DB_SCENETYPE_IFTT && isEnable){
			cJSON *item = cJSON_GetObjectItem(param,identifier);
			if(item != NULL){
				propertyValueType = kk_dm_get_property_type(deviceCode,identifier);
				res = kk_scene_check_value(compareType,compareValue,item,propertyValueType);
				if(res == SUCCESS_RETURN){
					INFO_PRINT("[%d]kk_scene_check_condition enter!!!\n",__LINE__);
					res = kk_scene_check_condition(sceneId);
					if(res == SUCCESS_RETURN){
						_kk_scene_lock();
						res = kk_scene_execute_action(sceneId);
						_kk_scene_unlock();
					}
				}
			}
		}

	}
	sqlite3_finalize(stmt);

	return res;
}
int kk_scene_iftt_check(const char*deviceCode,cJSON *param)
{
	return kk_scene_query_trigger_info(deviceCode,param);
}

int kk_scene_execute_scene(const char *sceneId)
{
	int res = 0;
	int sceneType = 0;
	int isEnable = 0;
	if(sceneId == NULL){
		return INVALID_PARAMETER;
	}
	res = kk_scene_get_scene_info(sceneId,&sceneType,&isEnable);
	if(res == SUCCESS_RETURN && sceneType == DB_SCENETYPE_SCENE && isEnable){
		res = kk_scene_check_condition(sceneId);
		if(res == SUCCESS_RETURN){
			_kk_scene_lock();
			res = kk_scene_execute_action(sceneId);
			_kk_scene_unlock();
		}
	}
	INFO_PRINT("[%d]kk_scene_execute_scene called!!!\n",__LINE__);
	return res;
}


