#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"

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_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, "result", result);
	rpc_cJSON_AddItemToObject(result_root, "id", id);

	char * str_result = rpc_cJSON_Print(result_root);
	return_value = kk_ipc_send(IPC_PLAT2MID, str_result, strlen(result_root)+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 *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);
            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;
    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, "id");
            if (id == NULL|| id->type == cJSON_String
            || id->type == cJSON_Number) {
            //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);
            }
        }
    }
    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);

}
