/************************************************************
*版权所有 （C）2020，公司（或个人）名称
*
*文件名称： kk_sub_db.c
*内容摘要： 网关设备和子设备设备表处理模块
*其他说明： 
*当前版本：  
*************************************************************/

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

extern sqlite3 *g_kk_pDb;

typedef struct {
    void *mutex;
	int  subDevNum;
    sqlite3 *pDb;	
} kk_subDb_ctx_t;
typedef enum{
	DB_IDX = 0,
	DB_ONLINE,
	DB_PRODUCTCODE,
	DB_DEVICECODE,
	DB_MAC,
	DB_FATHERDEVICECODE,
	DB_VERSION,
	DB_AUTH,
	DB_DEVTYPE,	
	DB_HEARTBEAT,	
	DB_PRODUCTTYPE,
	DB_SCENESUPPORT
};

static kk_subDb_ctx_t s_kk_subDb_ctx = {NULL,0,NULL};
static kk_subDb_ctx_t *_kk_subDb_get_ctx(void)
{
    return &s_kk_subDb_ctx;
}
static void _kk_subDb_lock(void)
{
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
    if (ctx->mutex) {
        HAL_MutexLock(ctx->mutex);
    }
}

static void _kk_subDb_unlock(void)
{
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
    if (ctx->mutex) {
        HAL_MutexUnlock(ctx->mutex);
    }
}

/************************************************************
*功能描述：设备表的初始化
*输入参数：无
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

static int kk_subDev_db_Init(void)
{
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

    //eUtils_LockLock(&sLock);
    _kk_subDb_lock();
	
	ctx->pDb = g_kk_pDb;
       
    INFO_PRINT("sub db Database opened\n");
    {
		const char *pSubDevTable = "CREATE TABLE IF NOT EXISTS SubDeviceInfo( \
			idx INTEGER, \
			isOffline INTEGER, \
			productCode varchar(33), \
			deviceCode varchar(33) UNIQUE, \
			mac varchar(17), \
			fatherDeviceCode varchar(33), \
			version varchar(33), \
			isAuth INTEGER, \
			devType INTEGER, \
			heartbeat INTEGER,\
			productType varchar(33),\
			sceneSupport INTEGER)";

	    char *pcErr;
        
//     DBG_vPrintf(DBG_SQL, "Execute SQL: '%s'\n", pConditionTableDef);
        
        if (sqlite3_exec(ctx->pDb, pSubDevTable, NULL, NULL, &pcErr) != SQLITE_OK)
        {
            ERROR_PRINT("Error creating table (%s)\n", pcErr);
            sqlite3_free(pcErr);
            //eUtils_LockUnlock(&sLock);
            _kk_subDb_unlock();
            return FAIL_RETURN;
        }
	 
    }

    //eUtils_LockUnlock(&sLock);
    _kk_subDb_unlock();
    return SUCCESS_RETURN;
}
/************************************************************
*功能描述：开机从设备数据库导出数据到内存
*输入参数：无
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/
static int _kk_load_subDevice(void)
{
	const char *searchCmd = "select * from SubDeviceInfo;";	
	sqlite3_stmt *stmt;	
	int deviceType = 0;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	int devId = 0,heartbeat = 0;
	int res = 0;
	_kk_subDb_lock();
	sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	   deviceType = sqlite3_column_int(stmt, DB_DEVTYPE);
	   res = dm_mgr_subdev_create(deviceType,
	   					sqlite3_column_text(stmt, DB_PRODUCTCODE),
	   					sqlite3_column_text(stmt, DB_DEVICECODE),
	   					sqlite3_column_text(stmt, DB_MAC),
	   					sqlite3_column_text(stmt, DB_FATHERDEVICECODE),
	   					KK_DEV_UNKNOW,&devId,&heartbeat);
	
	   if(res != SUCCESS_RETURN){
		   ERROR_PRINT("[%s][%d]dm_mgr_subdev_create FAIL!!!\n",__FUNCTION__,__LINE__);
	   }
	   else{
	   	   ctx->subDevNum++;
	   }
	   usleep(100000);
	   // send the topc info  
	   iotx_dm_subscribe(devId);
	   
	   //sync the data from property db
	   kk_property_sync_values(sqlite3_column_text(stmt, DB_DEVICECODE));
	   usleep(100000);

	   if(deviceType == KK_DM_DEVICE_GATEWAY){
			kk_dm_gw_status_check_push(sqlite3_column_text(stmt, DB_DEVICECODE));
	   }
	   //post the property to cloud
	   //dm_msg_thing_property_post_all(sqlite3_column_text(stmt, DB_DEVICECODE));
	    //kk_dm_ota_report_version(devId,sqlite3_column_text(stmt, DB_VERSION));//post version
	   //usleep(100000);
    }	
	sqlite3_finalize(stmt);
	_kk_subDb_unlock();
	return SUCCESS_RETURN;

}
/************************************************************
*功能描述：设备数据模块初始化
*输入参数：无
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDb_init(void)
{
	int res = 0;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	
    /* Create Mutex */
    ctx->mutex = HAL_MutexCreate();
    if (ctx->mutex == NULL) {
        return FAIL_RETURN;
    }

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

	}
	_kk_load_subDevice();
	return SUCCESS_RETURN;
}
/************************************************************
*功能描述：检查设备是否有重复
*输入参数：deviceCode：设备deviceCode
*输出参数：无
*返 回 值：1:存在；0：不存在
*其他说明：
*************************************************************/

static int _kk_check_subDev_exist(const char* deviceCode)
{
	int isExist = 0;
	sqlite3_stmt *stmt;
	char *pmac = NULL;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	const char *searchCmd = "select * from SubDeviceInfo;";	
	_kk_subDb_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){
	   pmac = sqlite3_column_text(stmt, DB_DEVICECODE);
	   if(!strcmp(deviceCode,pmac))
	   
{
		 isExist = 1;
		 break;
	   }
    }
    INFO_PRINT("\n");
	sqlite3_finalize(stmt);
	_kk_subDb_unlock();
	return isExist;

}

/************************************************************
*功能描述：保存设备信息到数据库
*输入参数：devType：设备类型
          productCode：产品Id
          deviceCode:设备deviceCode
          fatherDeviceCode:上一级网关的deviceCode
          mac: mac地址
          version：版本号
          heartbeat：心跳时间

*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_insert_db(int devType,char productCode[PRODUCT_CODE_MAXLEN], \
										char deviceCode[DEVICE_CODE_MAXLEN],char fatherDeviceCode[DEVICE_CODE_MAXLEN],char mac[DEVICE_MAC_MAXLEN],char version[DEVICE_VERSION_MAXLEN],int heartbeat)
{
	const char *insertCmd = "insert into SubDeviceInfo (idx,isOffline,productCode,deviceCode,mac,fatherDeviceCode,version,isAuth,devType,heartbeat,sceneSupport) \
								values ('%d','%d', '%s','%s','%s','%s','%s','%d','%d','%d','%d');";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();


	if(_kk_check_subDev_exist((const char*)deviceCode) == 1)
	{
		WARNING_PRINT("[%s][%d] DATA ALREADY EXIST!!!\n",__FUNCTION__,__LINE__);
		return SUCCESS_RETURN;
	}
	_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf(insertCmd,ctx->subDevNum,0,productCode,deviceCode,mac,fatherDeviceCode,version,0,devType,heartbeat,0);	

	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);
	ctx->subDevNum++;
	_kk_subDb_unlock();
	return SUCCESS_RETURN;
}
										
/************************************************************
*功能描述：删除设备信息
*输入参数：deviceCode:设备deviceCode
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/
int kk_subDev_delete_by_dcode(char deviceCode[DEVICE_CODE_MAXLEN])
{
	const char *deleteCmd = "delete from SubDeviceInfo where deviceCode = '%s';";
	char *sqlCmd = NULL;
	int rc = 0;
	char *zErrMsg = 0;
	kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

	_kk_subDb_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_subDb_unlock();
	return SUCCESS_RETURN;
}

/************************************************************
*功能描述：更新是否在线信息
*输入参数：deviceCode:设备deviceCode
          isOffline：1，离线；0，在线
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_update_offline(int isOffline,const char *deviceCode)
{
	char *sqlCmd = NULL;
	int len =0;
	int rc = 0;
	char *zErrMsg = 0;
	kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

	//_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf("UPDATE SubDeviceInfo SET isOffline=%d  WHERE deviceCode= '%s'",isOffline,deviceCode);	
	INFO_PRINT("kk_subDev_update_offline 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;
}

/************************************************************
*功能描述：更新设备类型
*输入参数：deviceCode:设备deviceCode
          productType：设备类型
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_update_productType(char *productType,const char *deviceCode)
{
	char *sqlCmd = NULL;
	int len =0;
	int rc = 0;
	char *zErrMsg = 0;
	kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

	//_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf("UPDATE SubDeviceInfo SET productType='%s'  WHERE deviceCode= '%s'",productType,deviceCode);	
	INFO_PRINT("kk_subDev_update_productType 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;
}

/************************************************************
*功能描述：更新设备注册标记
*输入参数：deviceCode:设备deviceCode
          isAuth：1，已注册；0，未注册
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/
int kk_subDev_update_auth(int isAuth,const char *deviceCode)
{
	char *sqlCmd = NULL;
	int len =0;
	int rc = 0;
	char *zErrMsg = 0;
	kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

	_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf("UPDATE SubDeviceInfo SET isAuth=%d  WHERE deviceCode= '%s'",isAuth,deviceCode);	
	INFO_PRINT("kk_subDev_update_auth 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;
}

/************************************************************
*功能描述：更新内场景支持，此接口只针对网关设备有效
*输入参数：deviceCode:设备deviceCode
          sceneSupport：1，支持内场景；0，不支持
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/
int kk_subDev_update_sceneSupport(int sceneSupport,const char *deviceCode)
{
	char *sqlCmd = NULL;
	int len =0;
	int rc = 0;
	char *zErrMsg = 0;
	kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();

	_kk_subDb_lock();
	sqlCmd = sqlite3_mprintf("UPDATE SubDeviceInfo SET sceneSupport=%d  WHERE deviceCode= '%s'",sceneSupport,deviceCode);	
	INFO_PRINT("kk_subDev_update_sceneSupport 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;
}


/************************************************************
*功能描述：主机重新上线后向云端上报属性
*输入参数：无
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_send_property_get_from_db(void)
{
	const char *searchCmd = "select * from SubDeviceInfo;";	
	sqlite3_stmt *stmt;	
	int isOffline = 0;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	int res = 0;
	dm_mgr_dev_node_t *node = NULL;	
	_kk_subDb_lock();
	sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
	while(sqlite3_step(stmt) == SQLITE_ROW){
	   isOffline = sqlite3_column_int(stmt, DB_DEVTYPE);
	   if(isOffline == KK_DEV_ONLINE){
		   res = dm_mgr_get_device_by_devicecode(sqlite3_column_text(stmt, DB_DEVICECODE), &node);
		   if (res != SUCCESS_RETURN) {
		       continue;
		   }
		   iotx_dm_dev_online(node->devid);

		   dm_msg_thing_property_post_all(sqlite3_column_text(stmt, DB_DEVICECODE));
	   }
    }	
	sqlite3_finalize(stmt);
	_kk_subDb_unlock();
	return SUCCESS_RETURN;


}
/************************************************************
*功能描述：主机断电重启后向网关获取所有子设备属性
*输入参数：fatherDeviceCode：网关deviceCode
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_send_property_get(const char *fatherDeviceCode)
{
	int res = 0;
	sqlite3_stmt *stmt;
	char *sqlCmd = NULL;
	char *pDeviceCode = NULL;
	dm_mgr_dev_node_t *node = NULL;		
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	const char *searchCmd = "select * from SubDeviceInfo WHERE fatherDeviceCode = '%s'";	

	if(fatherDeviceCode == NULL){
		ERROR_PRINT("ERROR [%s][%d]\n",__FUNCTION__,__LINE__);
		return INVALID_PARAMETER;
	}
	sqlCmd = sqlite3_mprintf(searchCmd,fatherDeviceCode);	
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &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);
	   res = dm_mgr_get_device_by_devicecode(pDeviceCode,&node);
	   if (res != SUCCESS_RETURN) {
		   ERROR_PRINT("ERROR [%s][%d] res:%d\n",__FUNCTION__,__LINE__,res);
		   continue;
	   }
	    /*休眠设备，不需要下发获取属性*/
	   if(node->dormancyDev){
			continue;
	   }
	   /*发送之前先置为KK_DEV_UNKNOW*/
	   node->isOffline = KK_DEV_UNKNOW;
	   kk_msg_execute_property_get(node);
	   usleep(100000);
    }

	sqlite3_finalize(stmt);
	sqlite3_free(sqlCmd);
	return SUCCESS_RETURN;


}

/************************************************************
*功能描述：检查网关是否支持内场景
*输入参数：deviceCode：网关deviceCode
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/

int kk_subDev_check_scene_support(const char *deviceCode)
{
	int res = 0;
	sqlite3_stmt *stmt;
	char *sqlCmd = NULL;
	char *pDeviceCode = NULL;
	
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	const char *searchCmd = "select * from SubDeviceInfo WHERE devType= '%d' and sceneSupport = '%d'";	

	if(deviceCode == NULL){
		ERROR_PRINT("ERROR [%s][%d]\n",__FUNCTION__,__LINE__);
		return INVALID_PARAMETER;
	}

	sqlCmd = sqlite3_mprintf(searchCmd,KK_DM_DEVICE_GATEWAY,1);	
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &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);
	   if (!strcmp(deviceCode,pDeviceCode)) {
		  res = 1;
	   }
    }
	sqlite3_finalize(stmt);
	sqlite3_free(sqlCmd);
	return res;
}

/************************************************************
*功能描述：向所有同类设备执行动作，场景设置中用到
*输入参数：productType：设备类型
          sceneId：场景Id
          propertyName:属性名
          propertyValue：属性值
          type：执行类型
*输出参数：无
*返 回 值：0:成功；其他：失败
*其他说明：
*************************************************************/
int kk_subDev_set_action_by_productType(const char *productType,const char *sceneId,const char *propertyName,const char *propertyValue,const char *type)
{
	int res = 0;
	sqlite3_stmt *stmt;
	char *sqlCmd = NULL;
	char *pDeviceCode = NULL;
	dm_mgr_dev_node_t *node = NULL;
    kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
	const char *searchCmd = "select * from SubDeviceInfo WHERE productType= '%s'";	
	int idx = 0;
	int gw_support_scene = 0;
	kk_tsl_data_t *property = NULL;

	if(productType == NULL){
		ERROR_PRINT("ERROR [%s][%d]\n",__FUNCTION__,__LINE__);
		return INVALID_PARAMETER;
	}

	sqlCmd = sqlite3_mprintf(searchCmd,productType);	//switch jsut for test
	sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &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);
	   res = dm_mgr_get_device_by_devicecode(pDeviceCode,&node);
	   if (res != SUCCESS_RETURN) {
		   continue;
	   }
	   if(kk_subDev_check_scene_support(node->fatherDeviceCode) == 1){
			gw_support_scene = 1;
	   }	
	   else{
			gw_support_scene = 0;
	   }
	   if(!strcmp(productType,"switch")){

			for(idx = 0; idx < node->dev_shadow->property_number; idx++){
				property = (kk_tsl_data_t *)(node->dev_shadow->properties + idx);
				if(property == NULL){
					continue;
				}	
				if(strstr(property->identifier,"PowerSwitch") != NULL){
					if(gw_support_scene){
						kk_scene_action_detail_t info = {0};
						memcpy(info.deviceCode,node->deviceCode,strlen(node->deviceCode));
						memcpy(info.propertyName,property->identifier,strlen(property->identifier));
						memcpy(info.propertyValue,propertyValue,strlen(propertyValue));
						info.epNum = 1;
						info.delay = 0;
						kk_scene_action_add(node->fatherDeviceCode,sceneId,info);
					}
				   res = kk_scene_insert_scene_action(type,node->deviceCode,1,
						   property->identifier,propertyValue,0,sceneId,node->fatherDeviceCode);
				   if(res != SUCCESS_RETURN){
					   INFO_PRINT("kk_subDev_set_action_by_productType fail!!!\n");
					   //return res;
					   continue;
				   }					
				}
			}

	   }
	   else{
		   if(gw_support_scene){
			   kk_scene_action_detail_t info = {0};
			   memcpy(info.deviceCode,node->deviceCode,strlen(node->deviceCode));
			   memcpy(info.propertyName,propertyName,strlen(propertyName));
			   memcpy(info.propertyValue,propertyValue,strlen(propertyValue));
			   info.epNum = 1;
			   info.delay = 0;
			   kk_scene_action_add(node->fatherDeviceCode,sceneId,info);
		   }
		   res = kk_scene_insert_scene_action(type,node->deviceCode,1,
				   propertyName,propertyValue,0,sceneId,node->fatherDeviceCode);
		   if(res != SUCCESS_RETURN){
			   INFO_PRINT("kk_subDev_set_action_by_productType fail!!!\n");
			   //return res;
			   continue;
		   }	   
	   }
    }
	sqlite3_finalize(stmt);
	sqlite3_free(sqlCmd);
	return SUCCESS_RETURN;
}




