#include<stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include "kk_data_handle.h"
#include "kk_opcode.h"
#include "com_api.h"
#include "kk_data_mng.h"
#include "kk_lan_ctrl.h"
#include "kk_lan_sync.h"

const char DM_MSG_TO_MIDDWARE[] = "{\"msgtype\":\"%s\",\"productCode\":\"%s\",\"deviceCode\":\"%s\"}";
char *strrpl(char *in, char *out, int outlen, char *src, char *dst)
{
	char *p = in;
	unsigned int  len = outlen - 1;

	// 这几段检查参数合法性
	if((NULL == src) || (NULL == dst) || (NULL == in) || (NULL == out))
	{
		return NULL;
	}
	if((strcmp(in, "") == 0) || (strcmp(src, "") == 0))
	{
		return NULL;
	}
	if(outlen <= 0)
	{
		return NULL;
	}
	
	while((*p != '\0') && (len > 0))
	{
		if(strncmp(p, src, strlen(src)) != 0)
		{
			int n = strlen(out);
 
			out[n] = *p;
			out[n + 1] = '\0';
 
			p++;
			len--;
		}
		else
		{
			strcat(out, dst);
			p += strlen(src);
			len -= strlen(dst);
		}
	}
	return out;
}
static char * _kk_data_create(char *msgtype,const char *productCode,const char *deviceCode,const char *param)
{
	cJSON *root;
	cJSON *payload;
	cJSON* infoObj;
	char *out;
	char *infoStr = NULL;
	int infoStr_len = 0;
	int res = 0;
	char method[128] = {0};

	strrpl(msgtype+1,method,sizeof(method),"/",".");
	printf("method:%s\n",method);
	infoStr_len = strlen(DM_MSG_TO_MIDDWARE)+strlen(productCode)+strlen(deviceCode)+strlen(msgtype)+10;
	infoStr = malloc(infoStr_len);
	if(infoStr == NULL){
		ERROR_PRINT("[%s][%d]\n",__FUNCTION__,__LINE__);
		return NULL;
	}
	memset(infoStr,0x0,infoStr_len);
	snprintf(infoStr,infoStr_len,DM_MSG_TO_MIDDWARE,msgtype,productCode,deviceCode);

	root=cJSON_CreateObject();
	infoObj = cJSON_Parse(infoStr);
	cJSON_AddItemToObject(root, MSG_INFO_STR, infoObj);

	payload = cJSON_CreateObject();
	if(payload){
		cJSON_AddItemToObject(root, MSG_PAYLOAD_STR, payload);	
		cJSON_AddStringToObject(payload, "msgId", "*");
		cJSON_AddStringToObject(payload, "version", "1.0");
		cJSON_AddStringToObject(payload, "method", method);
		cJSON_AddStringToObject(payload, "params", param);
	}
	out=cJSON_Print(root);
	cJSON_Minify(out);
	cJSON_Delete(root);
	free(infoStr);
	INFO_PRINT("[out]%s\n",out);	
	
	return out;
	//free(out);	/* Print to text, Delete the cJSON, print it, release the string. */
}
void kk_handle_sync_info(void)
{
	char *send_data = NULL;
	send_data  = _kk_data_create(SYNC_MSG_TYPE,"*","*","*");
	if(send_data == NULL){
		return;
	}
	printf("kk_ipc_send........%s	\n",send_data);
	kk_ipc_send(IPC_APP2MID, send_data, strlen(send_data)+1);
	free(send_data);
}
static int kk_send_ack(cJSON *root,int sockfd)
{
	char *out = NULL;
	char *tmpBuf = NULL;
	out=cJSON_Print(root);
	cJSON_Minify((char*)out);
	tmpBuf = calloc(strlen(out) + 4,1);
	if(tmpBuf == NULL){
		free(out);
		return -1;
	}
	strcat(tmpBuf,"!");
	strcat(tmpBuf,out);
	strcat(tmpBuf,"$");
	printf("tmpBuf:%s\n",tmpBuf);
	send(sockfd, tmpBuf, strlen(tmpBuf), 0);
	free(out);
	free(tmpBuf);
	return 0; 
}

static int kk_heartbeat_ack(int sockfd)
{
	cJSON *root;
	root=cJSON_CreateObject();
	if(root){
		cJSON_AddStringToObject(root, "nodeid", "*");
		cJSON_AddStringToObject(root, "opcode", HEARTBEAT_OPCODE);
		cJSON_AddStringToObject(root, "status", "success");
		cJSON_AddStringToObject(root, "arg", "*");
		kk_send_ack(root,sockfd);
	}
	cJSON_Delete(root);
	return 0;
}
static int kk_loginccu_ack(cJSON *arg,int sockfd)
{
	cJSON *zkid;
	cJSON *root;
	cJSON *args;
	char *out = NULL;
	char *tmpBuf = NULL;
	if(arg == NULL){
		return -1;
	}
	char ccuid[32] = {0};
	kk_lan_get_ccuid(ccuid);	
	zkid = cJSON_GetObjectItem(arg, ZKID_STRING);
	if(zkid != NULL){
		if(strstr(ccuid,zkid->valuestring) != NULL){
			root=cJSON_CreateObject();
			if(root){
				args = cJSON_CreateObject();
				if(args){
					cJSON_AddItemToObject(root, "arg", args);
					cJSON_AddStringToObject(args, "error_code", "0");
					cJSON_AddStringToObject(args, "seq", "");
					cJSON_AddStringToObject(args, "zkid", zkid->valuestring);
				}
				cJSON_AddStringToObject(root, "nodeid", "*");
				cJSON_AddStringToObject(root, "opcode", LOGIN_OPCODE);
				cJSON_AddStringToObject(root, "status", "success");
				kk_send_ack(root,sockfd);
			}
			cJSON_Delete(root);
		}
	}else{
		ERROR_PRINT("data error...\n");
		return -1;
	}
	return 0;
}

//构建hw信息
static cJSON *kk_zb_dev_hw_info_build(const char *deviceCode,cJSON * productCode,cJSON * online,const char *hw_ver,const char *sw_ver)
{
	cJSON *item;
	char mac[32] = {0};
	int online_status = 0;
	int pid;
	if(mac==NULL){
		return NULL;
	}
	
	item = cJSON_CreateObject();
	if(item){
		if(hw_ver!=NULL){
			cJSON_AddStringToObject(item, "hardware_version", hw_ver);
		}else{
			cJSON_AddStringToObject(item, "hardware_version", "");
		}
		_deviceCode_switchto_mac(deviceCode,mac);

		if(online==0){
			online_status = 1; //在线
		}else if(online==1){
			online_status = 2; //离线
		}else{
			online_status = 0; //未知
		}
		pid = atoi(productCode);
		
		cJSON_AddStringToObject(item, "mac", mac);
		cJSON_AddNumberToObject(item, "online_status",online_status);
		cJSON_AddNumberToObject(item, "product_id", pid);

		if(hw_ver!=NULL){
			cJSON_AddStringToObject(item, "version", sw_ver);
		}else{
			cJSON_AddStringToObject(item, "version", "");
		}
	}
	return item;
}

static void _kk_zb_devs_hw_ack(int sockfd,cJSON *arg)
{
	cJSON *root;
	root=cJSON_CreateObject();
	if(root){
		cJSON_AddStringToObject(root, "nodeid", "*");
		cJSON_AddStringToObject(root, "opcode", GET_ZB_DEVS_HW_INFO_OPCODE);
		cJSON_AddStringToObject(root, "status", "success");
		cJSON_AddItemToObject(root, "arg", arg);
		kk_send_ack(root,sockfd);
	}
	cJSON_Delete(root);
}

int kk_zb_devs_hw_ack_all(int sockfd)
{
	kk_map_dev_ctx *ctx = _kk_map_dev_ctx();
	kk_map_dev_node_t *search_node = NULL;
	cJSON *array = cJSON_CreateArray();
	cJSON *item;

	list_for_each_entry(search_node, &ctx->dev_list, linked_list, kk_map_dev_node_t) {
		if((item = kk_zb_dev_hw_info_build(search_node->deviceCode,search_node->productCode,search_node->online_status,NULL,NULL))!=NULL){
			cJSON_AddItemToArray(array,item);
		}
	}
	_kk_zb_devs_hw_ack(sockfd,array);
	return -1;
}

int kk_zb_dev_hw_info_build_by_deviceCode(int sockfd,const char *deviceCode)
{
	kk_map_dev_ctx *ctx = _kk_map_dev_ctx();
	kk_map_dev_node_t *search_node = NULL;
	cJSON *item;
	unsigned char online_status = 0;
	int pid =-1;
	
	list_for_each_entry(search_node, &ctx->dev_list, linked_list, kk_map_dev_node_t) {
		if ( (strlen(search_node->deviceCode) == strlen(deviceCode)) &&
			(memcmp(search_node->deviceCode, deviceCode, strlen(deviceCode)) == 0)) {

			item = kk_zb_dev_hw_info_build(search_node->deviceCode,search_node->productCode,search_node->online_status,NULL,NULL);

			return item;
		}
	}
	return NULL;
}


static void kk_zb_devs_hw_ack(int sockfd,cJSON *conditions)
{
	int i,size;
	cJSON *type;
	cJSON *arg;
	
	char devCode[33] ={0};
	
	type = cJSON_GetObjectItem(conditions,"condition_type");
	if(type==NULL){
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		kk_zb_devs_hw_ack_all(sockfd);
	}else{
		if(strcmp(type->valuestring,"dev_list")){
			WARNING_PRINT("[type err]%s,%s\n",type->valuestring,"dev_list");
			return ;
		}
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		arg = cJSON_GetObjectItem(conditions,"condition_arg");
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		size = cJSON_GetArraySize(arg);
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		cJSON *qMac;
		cJSON *item;
		cJSON *array = cJSON_CreateArray();
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		for(i=0;i<size;i++){
			printf("[%s][%d]\n",__FUNCTION__,__LINE__);
			qMac=cJSON_GetArrayItem(arg,1);
			if(mac_switchto_deviceCode(qMac->valuestring,devCode)==0){
				if((item=kk_zb_dev_hw_info_build_by_deviceCode(sockfd,devCode))!=NULL){
					cJSON_AddItemToArray(array,item);
				}
			}
		}
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
		_kk_zb_devs_hw_ack(sockfd,array);
		printf("[%s][%d]\n",__FUNCTION__,__LINE__);
	}

	


	return 0;
}



int kk_data_handle(cJSON *json,int sockfd)
{
	cJSON *opcode;
	cJSON *arg;
	opcode = cJSON_GetObjectItem(json, OPCODE_STRING);
	if(opcode != NULL){
		if(strcmp(opcode->valuestring,LOGIN_OPCODE) == 0){
			arg = cJSON_GetObjectItem(json, ARG_STRING);
			kk_loginccu_ack(arg,sockfd);
		}else if(strcmp(opcode->valuestring,HEARTBEAT_OPCODE) == 0){
			kk_heartbeat_ack(sockfd);
		}else if(strcmp(opcode->valuestring,SYNC_OPCODE) == 0){
			kk_handle_sync_info();
		}else if(strcmp(opcode->valuestring,GET_ZB_DEVS_HW_INFO_OPCODE) == 0){
			cJSON *conditions = cJSON_GetObjectItem(json, OPCODE_STRING);
			printf("[%s][%d]\n",__FUNCTION__,__LINE__);
			kk_zb_devs_hw_ack(sockfd,conditions);
		}else{
			
			kk_ccu_opcode_handle(json);
		}
	}
	return 0;
}
static int kk_parse_syncinfo(cJSON *payload)
{
	cJSON *paramStr,*dataStr;
	cJSON *gwdevices,*subdevices;
	cJSON * gwitem,*properties;  
	cJSON *onlineStatus;
	char *gwdevicecode;
	char *deviceCode,*productCode;
	char *identifier,*valuetype;
	cJSON * subitem;
	cJSON * newccuItem;
	cJSON *valuejson;
	int value;

	printf("[%s][%d]\n",__FUNCTION__,__LINE__);

	kk_map_dev_deinit();


	paramStr = cJSON_GetObjectItem(payload, DATA_STRING);
	if(paramStr == NULL) return -1;	
	dataStr = cJSON_GetObjectItem(paramStr, DATA_STRING);
	if(dataStr == NULL) return -1;
	gwdevices = cJSON_GetObjectItem(dataStr, DEVICES_STRING);
	if(gwdevices == NULL) return -1; 
	gwitem = gwdevices->child;
	while(gwitem != NULL){

		subdevices = cJSON_GetObjectItem(gwitem,DEVICES_STRING);
		
		gwdevicecode = cJSON_GetObjectItem(gwitem,MSG_DEVICE_CODE_STR)->valuestring;
		if(subdevices != NULL){
			printf("[%s][%d]\n",__FUNCTION__,__LINE__);
			subitem = subdevices->child;

			
			while(subitem != NULL){
				
				deviceCode = cJSON_GetObjectItem(subitem,MSG_DEVICE_CODE_STR)->valuestring;
	
				productCode = cJSON_GetObjectItem(subitem,MSG_PRODUCT_CODE_STR)->valuestring;
	

				onlineStatus =  cJSON_GetObjectItem(subitem,MSG_ONLINE_STATUS_STR);
	

				properties = cJSON_GetObjectItem(subitem,MSG_PROPERTIES_STR);


				
				kk_map_dev_node_add(deviceCode,productCode,gwdevicecode,onlineStatus->valuestring);
				property_syn_deal(deviceCode,properties);



				subitem = subitem->next;
			}
		}

		gwitem = gwitem->next;
	}
	return 0;
}






int is_ccu_msg(cJSON *productCode,cJSON *deviceCode)
{
	if((strlen(productCode->valuestring)==strlen(KK_CCU_PRODUCTID) &&
		!strcmp(productCode->valuestring,KK_CCU_PRODUCTID)) &&
		(strlen(deviceCode->valuestring)==strlen(KK_CCU_ID) &&
		!strcmp(deviceCode->valuestring,KK_CCU_ID))){
		return 1;
	}
	return 0;
}

int is_arming_status_notify(cJSON *payload)
{
	cJSON *params = NULL;
	cJSON *ArmingState = NULL;
	if((params = cJSON_GetObjectItem(payload, MSG_PARAMS_STR))!=NULL){
		if((ArmingState = cJSON_GetObjectItem(payload, "ArmingState"))!=NULL){
			if(ArmingState->type==cJSON_String &&
				ArmingState->valuestring!=NULL){
				return atoi(ArmingState->valuestring);
			}
		}
	}

	return -1;
}


void KK_Data_FromMid(void* str,int len)
{
	cJSON *json;
	cJSON *info,*payload;
	cJSON *msgtype,*deviceCode,*productCode;

	printf("[midware->lan] len=%d,data=%s\n",len,str);

	if((json= cJSON_Parse(str)) == NULL) {
		return;
	}

	info = cJSON_GetObjectItem(json, MSG_INFO_STR);
	payload = cJSON_GetObjectItem(json, MSG_PAYLOAD_STR);

	if(info==NULL || payload==NULL ||
		info->type != cJSON_Object ||payload->type!= cJSON_Object){
		return;
	}
	
	msgtype = cJSON_GetObjectItem(info, MSG_TYPE_STR);
	productCode = cJSON_GetObjectItem(info, MSG_PRODUCT_CODE_STR);
	deviceCode= cJSON_GetObjectItem(info, MSG_DEVICE_CODE_STR);

	if(msgtype == NULL || productCode==NULL || deviceCode==NULL||
		msgtype->type != cJSON_String || productCode->type!= cJSON_String||deviceCode->type!= cJSON_String ||
		msgtype->valuestring==NULL|| productCode->valuestring==NULL || deviceCode->valuestring==NULL){

		return;
	}

	if (strstr(msgtype->valuestring,SYNC_MSG_TYPE_REPLY) != NULL){
		kk_parse_syncinfo(payload);
		kk_create_syncinfo_to_sdk(payload);
	}else if(strstr(msgtype->valuestring,"/thing/event/property/post")!= NULL){

		if(is_ccu_msg(productCode,deviceCode)){
			int type;
			if((type = is_arming_status_notify(payload))!=-1){
				arming_status_notify(type);
			}
		}else{
			property_post_deal(deviceCode->valuestring,payload);
		}


	}else if(strstr(msgtype->valuestring,"/thing/topo/delete")!= NULL){
		device_delete_sync(payload);
	}else if(strstr(msgtype->valuestring,"/thing/status/online")!= NULL){
		device_add_sync(payload);
	}
	
}




