#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 "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 "com_api.h"
#include "kk_test.h"

static struct jrpc_server my_server;


cJSON * test_func(jrpc_context * ctx, cJSON * params, cJSON *id);

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[]={
	{test_func,"test_func"},
	RPC_KK_TEST_FUNCTION_TABLE,
	RPC_NETWORK_FUNCTION_TABLE,
	RPC_COMMON_FUNCTION_TABLE,
	RPC_GLOBAL_COMMAND_FUNCTION_TABLE,
	RPC_COLOR_CONTROL_COMMAND_FUNCTION_TABLE,
	RPC_OnOff_COMMAND_FUNCTION_TABLE,
};

void rpcInterfaceParse(void)
{
	emberAfAppPrint( "Thread rpc Interface Parse create\n" );
	jrpc_server_init(&my_server, PORT);
	emberAfAppPrint("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++){
		emberAfAppPrint("i=%d,%s\r\n",i,rpc_table[i].name);
		jrpc_register_procedure(&my_server, rpc_table[i].func, rpc_table[i].name, NULL );
	}

	jrpc_server_run(&my_server);
	jrpc_server_destroy(&my_server);

}





cJSON * test_func(jrpc_context * ctx, cJSON * params, cJSON *id) {

	cJSON * item1 = rpc_cJSON_CreateObject();
	cJSON * item2 = rpc_cJSON_CreateObject();

	rpc_cJSON_AddNullToObject(item1,"Null");
	rpc_cJSON_AddTrueToObject(item1,"True");
	rpc_cJSON_AddFalseToObject(item1,"False");
	rpc_cJSON_AddNumberToObject(item1, "Number",12345);
	rpc_cJSON_AddStringToObject(item1, "String","hello world!");

	rpc_cJSON_AddNullToObject(item2,"1");
	rpc_cJSON_AddTrueToObject(item2,"2");
	rpc_cJSON_AddFalseToObject(item2,"3");
	rpc_cJSON_AddNumberToObject(item2, "4",12345);
	rpc_cJSON_AddStringToObject(item2, "5","hello world!");

	rpc_cJSON_AddItemToObject(item1,"hhhhhh",item2);

	return item1;
}

static int send_result_resp(cJSON * result,
		cJSON * id) {
	int return_value = 0;
	cJSON *result_root = rpc_cJSON_CreateObject();
	if (result)
		rpc_cJSON_AddItemToObject(result_root, "code", result);
	if(id){
		printf("id json:\n%s\n",id->valuestring);
		rpc_cJSON_AddItemToObject(result_root, "msgId", id);
	}

	char * str_result = rpc_cJSON_Print(result_root);
	printf("send json:\n%s\n",str_result);
	return_value = kk_ipc_send(IPC_PLAT2MID, str_result, strlen(str_result)+1);
	free(str_result);
	rpc_cJSON_Delete(result_root);
	return return_value;
}
        

static int send_error_resp(int code, char* message,
		cJSON * id) {
	int return_value = 0;
	cJSON *result_root = rpc_cJSON_CreateObject();
	cJSON *error_root = rpc_cJSON_CreateObject();
	rpc_cJSON_AddNumberToObject(error_root, "code", code);
	rpc_cJSON_AddStringToObject(error_root, "message", message);
	rpc_cJSON_AddItemToObject(result_root, "error", error_root);
	rpc_cJSON_AddItemToObject(result_root, "id", id);
	char * str_result = rpc_cJSON_Print(result_root);
    //printf("alla=========== :%d\n", strlen(str_result)+1);
	return_value = kk_ipc_send(IPC_PLAT2MID, str_result, strlen(str_result)+1);
    printf("send_error_resp:\n%s\n", str_result);
	free(str_result);
	rpc_cJSON_Delete(result_root);
	free(message);
	return return_value;
}

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 *method, *params, *id,*mac;
    method = rpc_cJSON_GetObjectItem(root, "method");
    if (method != NULL && method->type == cJSON_String) {
        params = rpc_cJSON_GetObjectItem(root, "params");
        if (params == NULL|| params->type == cJSON_Array
        || params->type == cJSON_Object) {
            id = rpc_cJSON_GetObjectItem(root, "msgId");
            if (id == NULL|| id->type == cJSON_String
            || id->type == cJSON_Number) {
				mac = rpc_cJSON_GetObjectItem(root, "mac");
				if(mac->type = cJSON_String){
					//We have to copy ID because using it on the reply and deleting the response Object will also delete ID
					cJSON * id_copy = NULL;
					if (id != NULL)
						id_copy =
								(id->type == cJSON_String) ? rpc_cJSON_CreateString(
									   id->valuestring) :
									   rpc_cJSON_CreateNumber(id->valueint);
					if (server->debug_level)
						printf("Method Invoked[2]: %s\n", method->valuestring);
					return invoke_procedure(server, method->valuestring,
							params, id_copy,mac);
				}
            }
        }
    }
    send_error_resp(JRPC_INVALID_REQUEST,
            strdup("The JSON sent is not a valid Request object."), NULL);
    return -1;
}


void _cb(void* data){
	if (data != NULL){
		printf("plat2mid_cb: %s RECEIVED \r\n", data); 

		cJSON *root;
		char *end_ptr = NULL;

		if ((root = rpc_cJSON_Parse_Stream(data, &end_ptr)) != NULL) {
			if (1) {
				char * str_result = rpc_cJSON_Print(root);
				printf("Valid JSON Received:\n%s\n", str_result);
				free(str_result);
			}

			if (root->type == cJSON_Object) {
				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));

	printf("getenv\r\n");
	char * debug_level_env = getenv("HOME");
	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;
}

void ipcHandle(void)
{
	emberAfAppPrint( "Thread rpc Interface Parse create\n" );
    _init_param(&my_server);
    kk_ipc_init(IPC_PLAT2MID, _cb);
	emberAfAppPrint("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++){
		emberAfAppPrint("i=%d,%s\r\n",i,rpc_table[i].name);
		jrpc_register_procedure(&my_server, rpc_table[i].func, rpc_table[i].name, NULL );
	}

    //handle procidure
    while(1){
        //
        usleep(20000);
    
    }
	//jrpc_server_run(&my_server);
	//jrpc_server_destroy(&my_server);

}


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_ipc_send(IPC_PLAT2MID, str_result, strlen(str_result)+1);
	free(str_result);
	return return_value;
}


#define ATTRIBUTE_BUFFER_REPORT_DATA_TYPE           2
// 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_ReportAttributesCallback(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;

	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);

	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);

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

		bufferPtr = bufferPtr + bufferSize;
		i = 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]);

		cJSON *item_attr = rpc_cJSON_CreateObject();
		rpc_cJSON_AddItemToArray(array_attr,item_attr);
		
		EmberAfAttributeId attributeId = HIGH_LOW_TO_INT(bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS]);


		EmberEUI64 eui64;
		if(emberAfDeviceTableGetEui64FromNodeId(nodeId,eui64)){
			if(clusterId==ZCL_ON_OFF_CLUSTER_ID && attributeId==ZCL_ON_OFF_ATTRIBUTE_ID){
				if(bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE]==ZCL_BOOLEAN_ATTRIBUTE_TYPE){
					uint8_t LightStatus = bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE+1];
					if(LightStatus==0||LightStatus==1){
						kk_rpc_report_LightStatus(eui64,LightStatus);
					}
				}
			}
		}

		rpc_cJSON_AddAttributeToObject(item_attr,attributeId);

		rpc_cJSON_AddDataTypeToObject(item_attr,bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE]);

		int dataLen = bufferSize-3-typeSize;
		rpc_cJSON_AddLengthToObject(item_attr,dataLen);
		rpc_cJSON_AddDataToObject(item_attr,&bufferTemp[ATTRIBUTE_BUFFER_REPORT_DATA_TYPE+1+typeSize],dataLen);

		free(bufferTemp);
	}

	//rpc_report_attribute(item);
	
	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;
	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]);



			rpc_cJSON_AddStatusToObject(item_attr,Status);
			EmberAfAttributeId attributeId = HIGH_LOW_TO_INT(bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_HIGH_BITS],bufferTemp[ATTRIBUTE_BUFFER_ATTRIBUTEID_LOW_BITS]);
			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);

			//rpc_read_response_process_callback(nodeId,ep,clusterId,attributeId,bufferPtr[3],dataLen,dataPtr);
			
			free(bufferTemp);
		}else{
			rpc_cJSON_AddStatusToObject(item_attr,Status);
			bufferSize += 2;
			emberAfAppPrintln("Status=%d\n",Status);
		}
		i = i + bufferSize;
	}

	rpc_read_attribute_response(item);

	return false;
}



static 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);
}
																	

void emberAfPluginDeviceTableRejoinDeviceCallback(EmberEUI64 nodeEui64)
{
	uint16_t deviceTableIndex = emberAfDeviceTableGetFirstIndexFromEui64(nodeEui64);
	if(deviceTableIndex == EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_INDEX){
		return ;
	}

	EmberAfPluginDeviceTableEntry *deviceTable = emberAfDeviceTablePointer();

	cJSON* nodeJson = rpc_reportDeviceState("rejoin",deviceTable[deviceTableIndex].eui64);
	rpc_printfJSON("rejoin",nodeJson);
	rpc_send_message(nodeJson,"device rejoin");
}

void emberAfPluginDeviceTableDeviceLeftCallback(EmberEUI64 nodeEui64)
{
	uint16_t deviceTableIndex = emberAfDeviceTableGetFirstIndexFromEui64(nodeEui64);

	if(deviceTableIndex == EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_INDEX){
		return ;
	}
	EmberAfPluginDeviceTableEntry *deviceTable = emberAfDeviceTablePointer();

	cJSON* nodeJson = rpc_reportDeviceState("left",deviceTable[deviceTableIndex].eui64);
	rpc_printfJSON("left",nodeJson);
	rpc_send_message(nodeJson,"device left");
}

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);
}

