
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>

#include "RPC_API.h"
#include "./rpc_api/inc/rpc_interface_parse.h"
#include "rpc_network_operate.h"
#include "rpc_global_cmd.h"
#include "rpc_colorControl.h"
#include "rpc_onoff.h"

#include "kk_test.h"
#include "kk_ncp_queue.h"
#include "kk_tsl_property_get.h"
//#include "kk_log.h"


static struct jrpc_server my_server;

void aaaBBB()
{
	char *ptr;
	const char *strings[3]={"PowerSwitch_1","PowerSwitch_2","PowerSwitch_3"};
	cJSON *params = rpc_cJSON_CreateObject();
	cJSON *id = rpc_cJSON_CreateObject();
	cJSON *mac = rpc_cJSON_CreateObject();

	cJSON *array = rpc_cJSON_CreateArray();
	rpc_cJSON_AddItemToArray(array,rpc_cJSON_CreateString("PowerSwitch_1"));
	rpc_cJSON_AddItemToArray(array,rpc_cJSON_CreateString("PowerSwitch_2"));
	rpc_cJSON_AddItemToArray(array,rpc_cJSON_CreateString("PowerSwitch_3"));

	rpc_cJSON_AddItemToObject(params, "identifier", array);
	rpc_cJSON_AddStringToObject(id, "msgId","123");
	rpc_cJSON_AddStringToObject(mac, "mac","1122334455667788");

	ptr = rpc_cJSON_Print(params);
	printf("params--->%s\n",ptr);
	free(ptr);
	ptr = rpc_cJSON_Print(id);
	printf("id--->%s\n",ptr);
	free(ptr);
	ptr = rpc_cJSON_Print(mac);
	mac = cJSON_GetObjectItem(mac, "mac");
	printf("mac--->%s,%s\n",ptr,mac->valuestring);
	free(ptr);
	kk_tsl_get_property_operation(&my_server, params,id,mac);
}

typedef cJSON(*rpc_function)(jrpc_context * ctx, cJSON * params, cJSON *id);
typedef struct{
	rpc_function	func;
	char *			name;
}rpc_table_s;

rpc_table_s rpc_table[]={
	RPC_KK_TEST_FUNCTION_TABLE,
};







static int send_result_resp(cJSON * result,
		cJSON * id) {
#if 0		
	int return_value = 0;
	cJSON *info_root = rpc_cJSON_CreateObject();
	if(info_root){
		rpc_cJSON_AddStringToObject(info_root, "msgType", "");
		rpc_cJSON_AddStringToObject(info_root, "productType", "");
	    rpc_cJSON_AddStringToObject(info_root, "productCode", "");
	    rpc_cJSON_AddStringToObject(info_root, "deviceCode", "");
	}
	cJSON *payload_root = rpc_cJSON_CreateObject();
	if(payload_root){
		rpc_cJSON_AddItemToObject(payload_root, "msgId", id);
		rpc_cJSON_AddItemToObject(payload_root, "code", result);
	    rpc_cJSON_AddStringToObject(payload_root, "data", "{}");
	}

	
	cJSON *result_root = rpc_cJSON_CreateObject();
	if(result_root){
		rpc_cJSON_AddItemToObject(result_root, "info", info_root);
		rpc_cJSON_AddItemToObject(result_root, "payload", payload_root);
	}

	char * str_result = rpc_cJSON_Print(result_root);
	printf("send json:\n%s\n",str_result);
	return_value = kk_sendData2CCU(str_result, strlen(str_result));
	free(str_result);	
	rpc_cJSON_Delete(result_root);
	return return_value;
#else
	return 0;
#endif
}
        

static int send_error_resp(int code, char* message,
		cJSON * id) {
#if 0		
	int return_value = 0;
	cJSON *edata;

	cJSON *info_root = rpc_cJSON_CreateObject();
	if(info_root){
		rpc_cJSON_AddStringToObject(info_root, "msgType", "");
		rpc_cJSON_AddStringToObject(info_root, "productType", "");
	    rpc_cJSON_AddStringToObject(info_root, "productCode", "");
	    rpc_cJSON_AddStringToObject(info_root, "deviceCode", "");
	}
	cJSON *payload_root = rpc_cJSON_CreateObject();
	if(payload_root){
		rpc_cJSON_AddItemToObject(payload_root, "msgId", id);
		rpc_cJSON_AddNumberToObject(payload_root, "code", code);
		edata = rpc_cJSON_CreateObject();
		if(edata){
			rpc_cJSON_AddStringToObject(edata, "message", message);
		}
	    rpc_cJSON_AddItemToObject(payload_root, "data", edata);	
	}

	
	cJSON *result_root = rpc_cJSON_CreateObject();
	if(result_root){
		rpc_cJSON_AddItemToObject(result_root, "info", info_root);
		rpc_cJSON_AddItemToObject(result_root, "payload", payload_root);
	}	
	char * str_result = rpc_cJSON_Print(result_root);
    //printf("alla=========== :%d\n", strlen(str_result)+1);
	return_value = kk_sendData2CCU(str_result, strlen(str_result));
    printf("send_error_resp:\n%s\n", str_result);
	free(str_result);	
	rpc_cJSON_Delete(result_root);
	free(message);
	return return_value;
#else
	return 0;
#endif
}

static int invoke_procedure(struct jrpc_server *server,
        char *name, cJSON *params, cJSON *id,cJSON *mac) {
    cJSON *returned = NULL;
    int procedure_found = 0;
    jrpc_context ctx;
    ctx.error_code = 0;
    ctx.error_message = NULL;
    int i = server->procedure_count;
    while (i--) {
        if (!strcmp(server->procedures[i].name, name)) {
            procedure_found = 1;
            ctx.data = server->procedures[i].data;
            returned = server->procedures[i].function(&ctx, params, id,mac);
            break;
        }
    }
    if (!procedure_found)
        return send_error_resp(JRPC_METHOD_NOT_FOUND,
                strdup("Method not found."), id);
    else {
        if (ctx.error_code)
            return send_error_resp(ctx.error_code, ctx.error_message, id);
        else
            return send_result_resp(returned, id);
    }
}

static int eval_request(struct jrpc_server *server, cJSON *root) {
    cJSON *params, *id,*mac,*info,*msgType,*payload;
	info = rpc_cJSON_GetObjectItem(root, "info"); 
	if(info != NULL){
		
		msgType = rpc_cJSON_GetObjectItem(info, "msgType");
		mac = rpc_cJSON_GetObjectItem(info, "deviceCode");		
	}
	payload	= rpc_cJSON_GetObjectItem(root, "payload"); 
	if(payload != NULL){
		
		params = rpc_cJSON_GetObjectItem(payload, "params");
		id = rpc_cJSON_GetObjectItem(payload, "msgId");
	} 
	if(id != NULL && params != NULL && msgType != NULL && mac != NULL){
		
		cJSON * id_copy = NULL;
		id_copy = (id->type == cJSON_String) ? rpc_cJSON_CreateString(id->valuestring) : \
					   rpc_cJSON_CreateNumber(id->valueint);
		return invoke_procedure(server, msgType->valuestring,
							params, id_copy,mac);	

	}
	

    send_error_resp(JRPC_INVALID_REQUEST,
            strdup("The JSON sent is not a valid Request object."), NULL);
    return -1;
}

#define KK_THING_OTA_DEVICE_UPGRADE	         "/ota/device/upgrade"

void ncp_queue_tick(void)
{
	cJSON *root = NULL;
	char *str;
	if(ncp_queue_dequeue(REV_MSG,&root)==0){

		str = rpc_cJSON_Print(root);
		printf("Dequeue cJson = %s\n",str);
		free(str);
		eval_request(&my_server, root);
		rpc_cJSON_Delete(root);
	}
}
void _cb(void* data, int len, char* chlmark){
	if (data != NULL){
		printf("plat_cb: %s [%d]RECEIVED \r\n", data,len); 
		cJSON *root;
		char *end_ptr = NULL;

		if ((root = rpc_cJSON_Parse_Stream(data, &end_ptr)) != NULL) {

			if (root->type == cJSON_Object) {
                cJSON* info = rpc_cJSON_GetObjectItem(root, "info"); 
                
			    if (info!=NULL&&strstr(rpc_cJSON_GetObjectItem(info, "msgType")->valuestring,KK_THING_OTA_DEVICE_UPGRADE) != NULL){
			         
                    kk_ota_process(root);
			    }else{
			    	ncp_queue_enqueue(REV_MSG,(void *)root);
				    //eval_request(&my_server, root);
				}
			}
			//shift processed request, discarding it

			//rpc_cJSON_Delete(root);
		} else {
            if (1) {
				printf("INVALID JSON Received:\n---\n%s\n---\n",
						data);
			}
            send_error_resp(JRPC_PARSE_ERROR,
					strdup(
							"Parse error. Invalid JSON was received by the server."),
					NULL);

		}
	}
}

int _init_param(struct jrpc_server *server) {
	memset(server, 0, sizeof(struct jrpc_server));
	//kk_zlog_init("paltform");

	printf("getenv\r\n");
	char * debug_level_env = getenv("JRPC_DEBUG");
	printf("getenv(JRPC_DEBUG):%s\n", server->debug_level);
	if (debug_level_env == NULL)
		server->debug_level = 0;
	else {
		server->debug_level = strtol(debug_level_env, NULL, 10);
		printf("JSONRPC-C Debug level %d\n", server->debug_level);
	}
	server->debug_level = 1;
	return 0;
}

char g_mac[19] = {0};
char* kk_get_gw_mac(){
    int cnt = 0;
    EmberEUI64 eui64;
	emberAfGetEui64(eui64);
	while(eui64[0] ==0 && eui64[1] ==0  && eui64[2] == 0 && cnt++ < 3){
        sleep(1);
	}
	if (eui64[0] ==0 && eui64[1] ==0  && eui64[2] == 0){
        printf("get gw mac error !!! \n");
        return NULL;
	}
	rpc_eui64ToString(eui64,g_mac);
	return g_mac;
}


int search_ccu(char devcode[33], char ip[16], int* port){
    
    char sendCmdFmt[] = "search_kk_ccu|deviceCode=%s;protocol=%s";
    char sendMessage[128] = {0};
    char revMessage[128] = {0};
    int sock;
    int sk_recv;
    int iSendbytes;
    int iOptval = 1;
    int flag;
    int iAddrLength;
    int recvLen = 0;
    struct sockaddr_in Addrto;
    struct sockaddr_in AddrRev;
	
    char* macString = kk_get_gw_mac();
	if (macString == NULL){
        printf("[%s] get mac fail\n",__FUNCTION__);
        return -1;
	}

    sprintf(sendMessage,sendCmdFmt,macString/*GW_DEVICE_CODE*/,GW2CCU_PROTOCOL);

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("[%s] socket fail\n",__FUNCTION__);
        return -1;
    }

    if ((sk_recv = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("[%s] socket sk_recv fail\n",__FUNCTION__);
        close(sock);
        return -1;
    }
    
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &iOptval, sizeof(int)) < 0)
    {
        printf("[%s] setsockopt failed\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;
    }

     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iOptval, sizeof(int)) < 0)
    {
        printf("[%s] setsockopt failed\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;
    }
    

    if (setsockopt(sk_recv, SOL_SOCKET, SO_REUSEADDR, &iOptval, sizeof(int)) < 0)
    {
        printf("[%s] setsockopt failed\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;
    }
    
    flag = fcntl(sk_recv, F_GETFL, 0);
    if (flag < 0)
    {
        printf("[%s] fcntl failed.\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;;
    }
    flag |= O_NONBLOCK;
    if (fcntl(sk_recv, F_SETFL, flag) < 0)
    {
        printf("[%s] fcntl failed.\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;
    }
    memset(&Addrto, 0, sizeof(struct sockaddr_in));
    Addrto.sin_family = AF_INET;
    Addrto.sin_addr.s_addr = inet_addr("255.255.255.255");
    Addrto.sin_port = htons(25556);

    memset(&AddrRev, 0, sizeof(struct sockaddr_in));
    AddrRev.sin_family = AF_INET;
    AddrRev.sin_addr.s_addr = INADDR_ANY;
    AddrRev.sin_port = htons(25555);
    iAddrLength = sizeof(struct sockaddr);

    
    if (bind(sk_recv, (struct sockaddr *)&AddrRev, sizeof(AddrRev)) == -1)
    {
        printf("[%s] bind failed!\n",__FUNCTION__);
        close(sock);
        close(sk_recv);
        return -1;
    }

    while (1)
    {
        if ((iSendbytes = sendto(sock, sendMessage, strlen(sendMessage)+1, 0, (struct sockaddr*)&Addrto, sizeof(struct sockaddr))) == -1)
        {
            printf("[%s] sendto fail, errno=%d\n", __FUNCTION__,errno);
            close(sock);
            close(sk_recv);
            return -1;
        }
        
        sleep(1);
        recvLen = recvfrom(sk_recv, revMessage, sizeof(revMessage), 0, (struct sockaddr *)&AddrRev, &iAddrLength);
        if (recvLen > 0){
            printf("[%s] recv:%s\n", __FUNCTION__, revMessage);
            //"search_kk_ccu_ack|deviceCode=CCU_66666;ip=192.168.36.128;port=16565"
            if (strstr(revMessage, "search_kk_ccu_ack|") != NULL){
                char* ackConnet = revMessage + strlen("search_kk_ccu_ack|");
                char* tmp = NULL;
                char* endIdx = NULL;
                int itemLen = 0;
                int itemConnetLen = 0;

                
                tmp = strstr(ackConnet, "deviceCode=");
                itemLen = strlen("deviceCode=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    memcpy(devcode, tmp + itemLen,itemConnetLen);
                } 

                tmp = strstr(ackConnet, "ip=");
                itemLen = strlen("ip=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    memcpy(ip, tmp + itemLen,itemConnetLen);
                } 

                tmp = strstr(ackConnet, "port=");
                itemLen = strlen("port=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    char portstr[20] = {0};
                    memcpy(portstr, tmp + itemLen,itemConnetLen);
                    *port = atoi(portstr);
                } 

                
                //memcpy(ip, inet_ntoa(AddrRev.sin_addr), strlen(inet_ntoa(AddrRev.sin_addr)));
                printf(" recv deviceCode:%s ip:%s port:%d \n", devcode, ip, *port);
                break;
            }
        }
        
    }
    close(sock);
    close(sk_recv);
    return 0;
    
}

#define GW_PRODUCT_CODE "2"

void* _msg_topo_add(){
    char msgFmt[] = "{\"info\":{\"msgtype\":\"/thing/topo/add\",\"productCode\":\"%s\",\"deviceCode\":\"%s\"},\
                        \"payload\":{\"msgId\":\"%d\",\"version\":\"1.0\",\"params\":{\"deviceCode\":\"%s\",\"productCode\":\"%s\",\"mac\":\"%s\"}}}";
    //char msg[520] = {0};
    char* msg = NULL;
	char* macString = kk_get_gw_mac();
	if (macString == NULL){
        printf("[%s] get mac fail\n",__FUNCTION__);
        return NULL;
	}	
	msg = malloc(520);
    if (msg == NULL){
        printf("[%s] msg malloc fail\n",__FUNCTION__);
        return NULL;
	}
	
    sprintf(msg, msgFmt, GW_PRODUCT_CODE, macString, 1, macString, GW_PRODUCT_CODE,macString);
    //cJSON* msgObj = cJSON_Parse(msg);
    //char* outbuf = cJSON_Print(msgObj);
    //cJSON_Delete(msgObj);
    //return outbuf;
    return msg;
   
}


void* _msg_event_property_post(char ip[16], int port){
    char msgFmt[] = "{\"info\":{\"msgtype\":\"/thing/event/property/post\",\"productCode\":\"%s\",\"deviceCode\":\"%s\"},\
                      \"payload\":{\"msgId\":\"%d\",\"version\":\"1.0\",\"params\":{\"NetChannelState\":%d,\"WhiteListState\":%d,\
                        \"OnlineDetectionState\":%d,\"SN\":\"%s\",\"IPAddress\":\"%s\",\"MACAddress\":\"%s\",\"Port\":%d},\
                        \"time\":1524448722000,\"method\":\"thing.event.property.post\"}\
                     }";
    //char msg[620] = {0};
    char* msg = NULL;
	char* macString = kk_get_gw_mac();
    if (macString == NULL){
        printf("[%s] get mac fail\n",__FUNCTION__);
        return NULL;
	}

	msg = malloc(620);
    if (msg == NULL){
        printf("[%s] msg malloc fail\n",__FUNCTION__);
        return NULL;
	}
	
    sprintf(msg, msgFmt, GW_PRODUCT_CODE, macString,
                        1, 0, 0, 0, "12345", ip,macString,port);
    //cJSON* msgObj = cJSON_Parse(msg);
    //char* outbuf = cJSON_Print(msgObj);
    //cJSON_Delete(msgObj);
    //return outbuf;
    return msg;
}


int kk_connect_check(){
    if(strcmp(GW2CCU_PROTOCOL, "tcp") == 0){
        return kk_get_retry_num() > 20;
    }else{
        if (kk_ipc_get_senderrcnt(IPC_PLAT2MID) > 0){
            return kk_ipc_isconnect(IPC_PLAT2MID)==0?1:0;
        }
        return 0;
    }
}

void get_prey_handle(void)
{
	get_property_data_s *data;
	EmberStatus status;
	while(1){
		if(ncp_queue_dequeue(GET_PROPERTY,&data)==0){
			printf("\n================dequeue==================\n");
			printf("node = 0x%04X,ep=%d,clu=0x%04X,len=%d\n",data->node,data->ep,data->clu,data->len);
			for(int i=0;i<data->len;i++){
				printf("attr=0x%04X\n",data->attr[i]);
			}

			status = zclGReadAttrs(data->node,1,data->ep,false,data->clu,data->len,data->attr,true);

			free(data->attr);
			free(data);
		}
	}


}

void ipcHandle(void)
{
#ifdef GATEWAY_TYPE_NCP
    kk_print_info( "Thread rpc Interface Parse create\n" );

    _init_param(&my_server);

    char* macString = kk_get_gw_mac();
    if (macString == NULL){
        kk_print_error("[%s] get mac fail, exit pthread !!!!!!!!!!!!!!!!!\n",__FUNCTION__);
        return;
	}

    kk_ipc_init(IPC_PLAT2MID, _cb, macString, "127.0.0.1");

    kk_print_info("sizeof(rpc_table)=%d,sizeof(rpc_table_s)=%d,%d\n",sizeof(rpc_table),sizeof(rpc_table_s),sizeof(rpc_table)/sizeof(rpc_table_s));
	for(int i=0;i<sizeof(rpc_table)/sizeof(rpc_table_s);i++){
		kk_print_debug("i=%d,%s\r\n",i,rpc_table[i].name);
		jrpc_register_procedure(&my_server, rpc_table[i].func, rpc_table[i].name, NULL );
	}

    //send add gw to ccu
    char* outbuf = _msg_topo_add(); 
    if (outbuf == NULL){
        kk_print_error("[%s] topo add msg failed, exit\n",__FUNCTION__);
        return;
    }	

    kk_print_info("check nanomsg is connect(%d) \n", kk_ipc_isconnect(IPC_PLAT2MID));
	kk_ipc_send(IPC_PLAT2MID, outbuf, strlen(outbuf) + 1);
    free(outbuf);


    char* postmsg = _msg_event_property_post("127.0.0.1",0);
    if (postmsg == NULL){

        kk_print_error("[%s] property_post msg failed\n",__FUNCTION__);
        return;
    }			
    kk_ipc_send(IPC_PLAT2MID, postmsg, strlen(postmsg) + 1);
    free(postmsg);

	//handle procidure
    while(1){
        //
        usleep(20000);
        
    }
#else

    char deviceCode[33] = {0};
    char ip[16] = {0};
    int port = 0;
	kk_print_info( "Thread rpc Interface Parse create\n" );
	search_ccu(deviceCode, ip, &port);
    _init_param(&my_server);
	char* macString = kk_get_gw_mac();
    if (macString == NULL){
        kk_print_error("[%s] get mac fail, exit pthread !!!!!!!!!!!!!!!!!\n",__FUNCTION__);
        return;
	}	
    if(strcmp(GW2CCU_PROTOCOL, "tcp") == 0){
        kk_tcp_client_init(ip, port, _cb);
    }else{
        kk_ipc_init(IPC_PLAT2MID, _cb, macString, ip);
    }
    
	kk_print_info("sizeof(rpc_table)=%d,sizeof(rpc_table_s)=%d,%d\n",sizeof(rpc_table),sizeof(rpc_table_s),sizeof(rpc_table)/sizeof(rpc_table_s));
	for(int i=0;i<sizeof(rpc_table)/sizeof(rpc_table_s);i++){
		kk_print_info("i=%d,%s\r\n",i,rpc_table[i].name);
		jrpc_register_procedure(&my_server, rpc_table[i].func, rpc_table[i].name, NULL );
	}

	//send add gw to ccu
    char* outbuf = _msg_topo_add(); 
    if (outbuf == NULL){

        kk_print_error("[%s] topo add msg failed, exit\n",__FUNCTION__);
        return;
    }	
    if (strcmp(GW2CCU_PROTOCOL, "tcp") != 0){
        kk_print_info("check nanomsg is connect(%d) \n", kk_ipc_isconnect(IPC_PLAT2MID));
    }
	kk_sendData2CCU(outbuf, strlen(outbuf));
    free(outbuf);

    int cnt = 0;
    //handle procidure
    while(1){
        //
        usleep(20000);
        cnt++;
        if (cnt == 2){
            sleep(1);
            char gwIp[17] = {0};
            HAL_Get_IP(gwIp,NULL);
            char* postmsg = _msg_event_property_post(gwIp,port);
            if (postmsg == NULL){

                kk_print_error("[%s] property_post msg failed\n",__FUNCTION__);
                continue;
            }			
            kk_sendData2CCU(postmsg, strlen(postmsg));
            free(postmsg);
        }

        if (kk_connect_check()){
            //discover ccu
            search_ccu(deviceCode, ip, &port);
            if(strcmp(GW2CCU_PROTOCOL, "tcp") == 0){
                kk_tcp_client_init(ip, port, _cb);
            }else{
                kk_ipc_deinit(IPC_PLAT2MID);
                kk_ipc_init(IPC_PLAT2MID, _cb, macString/*GW_DEVICE_CODE*/, ip);
            }

            //send add gw to ccu
            outbuf = _msg_topo_add(); 
            if (outbuf == NULL){

                kk_print_error("[%s] topo add msg failed, exit\n",__FUNCTION__);
                return;
            }
            kk_sendData2CCU(outbuf, strlen(outbuf));
            free(outbuf);
            cnt = 0;
            kk_reset_retry_num();
        }
        
    
    }
#endif

}


int jrpc_send_msg(cJSON * msgJson) {
	int return_value = 0;

	char * str_result = rpc_cJSON_Print(msgJson);

	emberAfAppPrintln("send json:\n%s\n",str_result);




	return_value = kk_sendData2CCU(str_result, strlen(str_result));
	free(str_result);
	return return_value;
}

#define ATTRIBUTE_BUFFER_ATTRIBUTEID_ID				1
#define ATTRIBUTE_BUFFER_REPORT_DATA_TYPE			2
#define ATTRIBUTE_BUFFER_REPORT_DATA_VALUE			3

// Attribute reading buffer location definitions
#define ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS  0
#define ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS 1
#define ATTRIBUTE_BUFFER_SUCCESS_CODE        2
#define ATTRIBUTE_BUFFER_DATA_TYPE           3
#define ATTRIBUTE_BUFFER_DATA_START          4

static void rpc_send_message(cJSON *data,char *method)
{
	cJSON *item = rpc_cJSON_CreateObject();
	rpc_cJSON_AddStringToObject(item, "jsonrpc", "2.0");
	rpc_cJSON_AddStringToObject(item, "method",method);
	rpc_cJSON_AddItemToObject(item, "params", data);

	char* p = rpc_cJSON_Print(item);
	emberAfAppPrintln("send send json:\n%s\n",p);
	free(p);
	
	jrpc_send_msg(item);

}
void rpc_read_attribute_response(cJSON *data)
{
	rpc_send_message(data,"read_attribute_response");
}

void rpc_report_attribute(cJSON *data)
{
	rpc_send_message(data,"report_attribute");
}
void rpc_report_devices(cJSON *data)
{
	rpc_send_message(data,"report_devices");
}
void rpc_control_devices(cJSON *data,char *method)
{
	rpc_send_message(data,method);
}
bool rpc_IasZoneClusterZoneStatusChangeNotificationCallback(uint16_t zoneStatus,
                                                               uint8_t extendedStatus,
                                                               uint8_t zoneId,
                                                               uint16_t delay)
{
	EmberEUI64 nodeEui64;
	EmberNodeId nodeId = emberAfCurrentCommand()->source;
	kk_print_debug("\n********************ChangeNotificationCallback**********************\n");
	emberAfDeviceTableGetEui64FromNodeId(nodeId, nodeEui64);
	kk_tsl_report_alarm(nodeEui64,zoneStatus,extendedStatus,zoneId,delay);
}

bool rpc_ReportAttributesCallback(EmberAfClusterId clusterId,
												uint8_t * buffer,
												uint16_t bufLen)
{
	EmberEUI64 nodeEui64;
	EmberAfAttributeId attributeId;
	EmberNodeId nodeId = emberAfCurrentCommand()->source;
	uint8_t ep = emberAfCurrentCommand()->apsFrame->sourceEndpoint;
	emberAfDeviceTableGetEui64FromNodeId(nodeId, nodeEui64);
	uint8_t * bufferTemp;
	uint8_t * bufferPtr = buffer;
	uint8_t i, bufferSize,typeSize;
	
	uint8_t dataLen,dataType;
	uint8_t *dataPtr;

	kk_print_debug("\n********************report callback**********************\n");
	emberAfAppPrint("[ ");
	emberAfAppPrintBuffer(buffer,bufLen,true);
	emberAfAppPrint("]\n");

	if (bufLen == 0) {
		emberAfAppPrintln("Report attributes callback: zero length buffer");
		return false;
	}

	kk_print_debug("\nmac:");
	emberAfPrintBigEndianEui64(nodeEui64);
	emberAfAppPrintln(",EP=%d,cluster=0x%04X\n",ep,clusterId);


	for (i = 0; i < bufLen - 3; ) {
		dataType = bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE];
		if(emberAfIsStringAttributeType(dataType)){
			dataLen = bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_VALUE ];
			typeSize = 1;
		}else if(emberAfIsLongStringAttributeType(dataType)){
			dataLen = HIGH_LOW_TO_INT(bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE + 2],
										bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE + 1]);
			typeSize = 2;
		}else {
			typeSize = 0;
			dataLen = emberAfGetDataSize(
							bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE]);
		}
		dataPtr = &bufferPtr[ATTRIBUTE_BUFFER_REPORT_DATA_VALUE];
		
		bufferSize = ATTRIBUTE_BUFFER_REPORT_DATA_VALUE + dataLen + typeSize;
		bufferTemp = (uint8_t*)malloc(bufferSize);
		memcpy(bufferTemp, bufferPtr, bufferSize);

		bufferPtr +=bufferSize;
		i +=bufferSize;
		
		emberAfAppPrintln("i=%d,bufferSize=%d\n",i,bufferSize);

		emberAfAppPrintln("Reported attribute: 0x%02X%02X, Type: %02X",
							bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],
							bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS],
							bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE]);

	
		attributeId = HIGH_LOW_TO_INT(bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS]);
		emberAfAppPrintln("attributeId=0x%x",attributeId);

		if(emberAfDeviceTableGetEui64FromNodeId(nodeId,nodeEui64)){
			emberAfAppPrintln("nodeId=0x%x",nodeId);
			kk_tsl_report_attribute(nodeEui64,ep,clusterId,attributeId,dataType,dataLen,dataPtr);
		}

	
		free(bufferTemp);
	}

	return false;
}

bool rpc_ReadAttributesResponseCallback(EmberAfClusterId clusterId,
														uint8_t *buffer,
														uint16_t bufLen)
{
	EmberEUI64 nodeEui64;
	EmberNodeId nodeId = emberAfCurrentCommand()->source;
	uint8_t ep = emberAfCurrentCommand()->apsFrame->sourceEndpoint;
	emberAfDeviceTableGetEui64FromNodeId(nodeId, nodeEui64);
	uint8_t * bufferTemp;
	uint8_t * bufferPtr = buffer;
	uint8_t i, bufferSize,typeSize;
	EmberAfAttributeId attributeId;
	uint8_t cnt=1;
	uint8_t Status;
	kk_print_debug("\n********************read attributes response callback**********************\n");
	emberAfAppPrint("[ ");
	emberAfAppPrintBuffer(buffer,bufLen,true);
	emberAfAppPrint("]\n");

	if (bufLen == 0) {
		emberAfAppPrintln("read attributes response callback: zero length buffer");
		return false;
	}

	kk_print_debug("\nmac:");
	emberAfPrintBigEndianEui64(nodeEui64);
	emberAfAppPrintln(",EP=%d,cluster=0x%04X\n",ep,clusterId);

	cJSON *item  = rpc_cJSON_CreateObject();
	cJSON *array_attr = rpc_cJSON_CreateObject();

	rpc_cJSON_AddMACToObject(item,nodeEui64);
	rpc_cJSON_AddNodeToObject(item,nodeId);
	rpc_cJSON_AddEndpointToObject(item,ep);
	rpc_cJSON_AddClusterToObject(item,clusterId);

	array_attr = rpc_cJSON_CreateArray();
	rpc_cJSON_AddItemToObject(item,"attributes",array_attr);

	cJSON *item_attr = rpc_cJSON_CreateObject();
	rpc_cJSON_AddItemToArray(array_attr,item_attr);

	//todo:check
	for (i = 0; i < bufLen; ) {
		Status = bufferPtr[2];

		
		if(Status == EMBER_ZCL_STATUS_SUCCESS){
			if(emberAfIsStringAttributeType(bufferPtr[3])){
				bufferSize = bufferPtr[4];
				typeSize = 1;
			}else if(emberAfIsLongStringAttributeType(bufferPtr[3])){
				bufferSize = HIGH_LOW_TO_INT(bufferPtr[5], bufferPtr[4]);
				typeSize = 2;
			}else {
				typeSize = 0;
				bufferSize = emberAfGetDataSize(
				bufferPtr[3]);
			}
			bufferSize = bufferSize + 4 + typeSize;
			bufferTemp = (uint8_t*)malloc(bufferSize);
			memcpy(bufferTemp, bufferPtr, bufferSize);

			bufferPtr = bufferPtr + bufferSize;
			
			emberAfAppPrintln("i=%d,bufferSize=%d\n",i,bufferSize);

			emberAfAppPrintln("Read attribute Response: 0x%02X%02X, Type: %02X",
							  bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],
							  bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS],
							  bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE]);

			attributeId = HIGH_LOW_TO_INT(bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS]);


			rpc_cJSON_AddStatusToObject(item_attr,Status);
			
			rpc_cJSON_AddAttributeToObject(item_attr,attributeId);
			
			rpc_cJSON_AddDataTypeToObject(item_attr,bufferTemp[3]);
			
			int dataLen = bufferSize-4-typeSize;
			rpc_cJSON_AddLengthToObject(item_attr,dataLen);
			uint8_t *dataPtr = &bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE+1+1+typeSize];
			rpc_cJSON_AddDataToObject(item_attr,dataPtr,dataLen);

			kk_read_attr_response(nodeId,
									ep,
									clusterId,
									attributeId,
									EMBER_ZCL_STATUS_SUCCESS,
									bufferPtr[3],
									dataLen,
									dataPtr);
			free(bufferTemp);
		}else{
			rpc_cJSON_AddStatusToObject(item_attr,Status);
			bufferSize += 3;
			emberAfAppPrintln("Status=0x%X\n",Status);
			attributeId = HIGH_LOW_TO_INT(bufferPtr[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],bufferPtr[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS]);
			kk_read_attr_response(nodeId,
									ep,
									clusterId,
									attributeId,
									Status,
									0,
									0,
									NULL);
		}
		i = i + bufferSize;
	}

	rpc_read_attribute_response(item);

	return false;
}



cJSON* rpc_reportDeviceState(char *state,EmberEUI64 eui64)
{
	char euiString[RPC_EUI64_STRING_LENGTH] = { 0 };
	cJSON* stateJSON;

	rpc_eui64ToString(eui64, euiString);

	stateJSON = rpc_cJSON_CreateObject();
	rpc_cJSON_AddStringToObject(stateJSON, "mac", euiString);
	rpc_cJSON_AddStringToObject(stateJSON, "status", state);
	return stateJSON;
}

void rpc_reportDeviceStateChange(EmberEUI64 eui64,uint8_t state)
{
	char euiString[RPC_EUI64_STRING_LENGTH] = { 0 };
	cJSON* stateChangeJson;

	rpc_eui64ToString(eui64, euiString);

	stateChangeJson = rpc_cJSON_CreateObject();
	rpc_cJSON_AddStringToObject(stateChangeJson, "mac", euiString);
	rpc_cJSON_AddNumberToObject(stateChangeJson, "deviceState", state);
	rpc_printfJSON("devicestatechange",stateChangeJson);
}

void emberAfPluginDeviceTableStateChangeCallback(EmberNodeId nodeId,
																	uint8_t state)
{
	EmberEUI64 nodeEui64;
	emberAfDeviceTableGetEui64FromNodeId(nodeId, nodeEui64);
	rpc_reportDeviceStateChange(nodeEui64, state);
}
																	





static cJSON* rpc_buildDeviceEndpoint(EmberEUI64 eui64, uint8_t endpoint)
{
	cJSON* deviceEndpointObj;
	char euiString[RPC_EUI64_STRING_LENGTH] = { 0 };
	deviceEndpointObj = rpc_cJSON_CreateObject();

	rpc_eui64ToString(eui64, euiString);
	rpc_cJSON_AddStringToObject(deviceEndpointObj, "mac", euiString);
	rpc_cJSON_AddNumberToObject(deviceEndpointObj, "ep", endpoint);
	return deviceEndpointObj;
}

static cJSON* rpc_buildDeviceEndpointWithClusterInfo(
	EmberEUI64 eui64,
	uint8_t endpoint,
	uint16_t *clusterIds,
	uint8_t clusterOutStartPosition)
{
	cJSON* deviceEndpointObj;
	cJSON* clusterInfoArray;
	cJSON* clusterInfoItem;
	uint16_t clusterIdIndex;
	char clusterIdString[RPC_CLUSTERID_STRING_LENGTH] = { 0 };

	clusterInfoArray = rpc_cJSON_CreateArray();
	deviceEndpointObj = rpc_buildDeviceEndpoint(eui64, endpoint);

	for (clusterIdIndex = 0;
			clusterIdIndex < EMBER_AF_PLUGIN_DEVICE_TABLE_CLUSTER_SIZE;
			clusterIdIndex++) {
		clusterInfoItem = rpc_cJSON_CreateObject();
		if (clusterIds[clusterIdIndex] != ZCL_NULL_CLUSTER_ID) {
			sprintf(clusterIdString, "0x%04X", clusterIds[clusterIdIndex]);
			rpc_cJSON_AddStringToObject(clusterInfoItem, "clusterId", clusterIdString);
			if (clusterIdIndex < clusterOutStartPosition) {
				rpc_cJSON_AddStringToObject(clusterInfoItem, "clusterType", "In");
			} else {
				rpc_cJSON_AddStringToObject(clusterInfoItem, "clusterType", "Out");
			}
			rpc_cJSON_AddItemToArray(clusterInfoArray, clusterInfoItem);
			clusterInfoItem = NULL;
		} else {
			rpc_cJSON_Delete(clusterInfoItem);
			clusterInfoItem = NULL;
			break;
		}
	}
	rpc_cJSON_AddItemToObject(deviceEndpointObj, "clusterInfo", clusterInfoArray);
	return deviceEndpointObj;
}

static cJSON* buildNodeJson(uint16_t nodeIndex)
{
	cJSON* nodeJson;
	cJSON* deviceEndpoint;
	char nodeIdString[RPC_NODEID_STRING_LENGTH] = { 0 };
	char* deviceTypeString;

	EmberAfPluginDeviceTableEntry *deviceTable = emberAfDeviceTablePointer();

	nodeJson = rpc_cJSON_CreateObject();
	rpc_nodeIdToString(deviceTable[nodeIndex].nodeId, nodeIdString);
	rpc_cJSON_AddStringToObject(nodeJson, "nodeId", nodeIdString);
	rpc_cJSON_AddNumberToObject(nodeJson,
								"deviceState",
								deviceTable[nodeIndex].state);
	deviceTypeString = rpc_createTwoByteHexString(deviceTable[nodeIndex].deviceId);
	rpc_cJSON_AddStringToObject(nodeJson, "deviceType", deviceTypeString);
	free(deviceTypeString);

	deviceEndpoint = rpc_buildDeviceEndpointWithClusterInfo(
		deviceTable[nodeIndex].eui64,
		deviceTable[nodeIndex].endpoint,
		deviceTable[nodeIndex].clusterIds,
		deviceTable[nodeIndex].clusterOutStartPosition);
	rpc_cJSON_AddItemToObject(nodeJson, "deviceEndpoint", deviceEndpoint);
	return nodeJson;
}



void rpc_reportDevices(void)
{
	uint16_t nodeIndex;
	cJSON* nodeJson;
	cJSON* devicesJson;
	cJSON* devicesJsonNodeArray;

	devicesJson = rpc_cJSON_CreateObject();
	devicesJsonNodeArray = rpc_cJSON_CreateArray();
	rpc_cJSON_AddItemToObject(devicesJson, "devices", devicesJsonNodeArray);

	for (nodeIndex = 0;
		nodeIndex < EMBER_AF_PLUGIN_DEVICE_TABLE_DEVICE_TABLE_SIZE;
		nodeIndex++) {
		if (emberAfDeviceTableGetNodeIdFromIndex(nodeIndex)
			!= EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_NODE_ID) {
			nodeJson = buildNodeJson(nodeIndex);
			rpc_cJSON_AddItemToArray(devicesJsonNodeArray, nodeJson);
			break;
		}
	}
	rpc_report_devices(devicesJson);
}