#include<stdio.h>
#include"com_api.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include "cJSON.h"
#include "kk_product.h"
#include "kk_tsl_common.h"
#include "kk_tsl_api.h"
#include "kk_dm_api.h"
#include "kk_dm_msg.h"
#include "kk_dm_mng.h"
#include "kk_log.h"
#include "kk_dm_queue.h"


char * g_filerToPlatTable[] = 
{
	{KK_REGISTER_TOPIC_REPLY},
	{KK_ADD_TOPIC_REPLY},
	{KK_LOGIN_TOPIC_REPLY},	
	{KK_THING_OTA_DEVICE_UPGRADE}, 
	{KK_THING_CLOUDSTATE_MSG}, 	
	
};
static int _kk_filter_to_plat(const char* msgtype)
{
	int i = 0,num = 0;
	num = sizeof(g_filerToPlatTable)/sizeof(char *);

	for(i =0; i < num; i++){
		if(strstr(msgtype,g_filerToPlatTable[i]))
		{
			return 1;
		}
	}
	return 0;
}
void mid_cb(void* data, int len){
	if (data != NULL){

        char *out;
        cJSON *json;
        cJSON *info_root,*info,*type;
        cJSON*payload;
		cJSON*deviceCode;
        int res;
        void* buf = malloc(len);
        memcpy(buf, data, len);
        res = dm_queue_msg_insert((void *)buf);
        if (res != SUCCESS_RETURN) {
            free(buf);
            //return FAIL_RETURN;
        }

    	json=cJSON_Parse(data);
    	if (!json) {
            printf("Error before: [%s]\n","cJSON_Parse");
        }
    	else
    	{
            info_root = cJSON_GetObjectItem(json, MSG_INFO_STR);
			deviceCode = cJSON_GetObjectItem(info_root, MSG_DEVICE_CODE_STR);				
			type = cJSON_GetObjectItem(info_root, MSG_TYPE_STR);
			if(_kk_filter_to_plat(type->valuestring)){
				cJSON_Delete(json);
				cJSON_Delete(info);
				return;
			}

			int devType = 0;
			dm_mgr_get_deviceType_by_mac(deviceCode->valuestring,&devType);
			if(devType == KK_DM_DEVICE_GATEWAY){
				kk_ipc_send_ex(IPC_MID2PLAT, data, strlen(data), deviceCode->valuestring);//send to gw itself
			}else if(devType == KK_DM_DEVICE_SUBDEV){
				dm_mgr_dev_node_t *gw_node = NULL;
				res = dm_mgr_get_device_by_mac(deviceCode->valuestring,&gw_node);
			    if (res != SUCCESS_RETURN) {
					ERROR_PRINT("res:%d\n",res);
					cJSON_Delete(json);
					cJSON_Delete(info);
			        return;
			    }		
				kk_ipc_send_ex(IPC_MID2PLAT, data, strlen(data), gw_node->fatherMac);//send to sub device
			}
			else{
				ERROR_PRINT("wrong type\n");
			}
            cJSON_Delete(json);
			cJSON_Delete(info);
            
	    }
	}
}

void mid2p_cb(void* data, int len, char* chalMark){
	if (data != NULL){
		//printf("mid2plat_cb: %s RECEIVED \r\n", data);
		int res = 0;
		void* buf = NULL;
		dm_queue_msg_t *queue_msg = NULL;
		queue_msg = malloc(sizeof(dm_queue_msg_t));
		if (queue_msg == NULL){
            ERROR_PRINT("mid2p_cb malloc queue_msg failed ");
            return;
		}
		buf = malloc(len);
		if (buf == NULL){
            ERROR_PRINT("mid2p_cb malloc buf failed ");
            return;
		}
		
        memcpy(buf, data, len);
        queue_msg->data = buf;
        memset(queue_msg->chalMark, 0, sizeof(queue_msg->chalMark));
        if(chalMark != NULL){
            memcpy(queue_msg->chalMark, chalMark, sizeof(chalMark));
        }
        res = dm_queue_msg_insert2((void *)queue_msg);
        if (res != SUCCESS_RETURN) {
            free(queue_msg);
            free(buf);
            return ;
        }
		
        //kk_ipc_send(IPC_MID2APP, data, len);
	}
}

void kk_platMsg_handle(void* data, char* chalMark){
    
    char *out;
    int res = 0;
    cJSON *json;
    cJSON *method;
    cJSON *params;
    cJSON *jsonPay;
    cJSON *proType;
    cJSON *proCode;
	cJSON *devCode;
    cJSON *mac;
    
    json=cJSON_Parse(data);
	if (!json) {
        WARNING_PRINT("Error before: [%s]\n","cJSON_Parse");
    }
	else{
        method = cJSON_GetObjectItem(json, "method");
        mac = cJSON_GetObjectItem(json, "mac");
        if (method != NULL && strcmp(method->valuestring, "thing.topo.add")==0){

                jsonPay = cJSON_GetObjectItem(json, "params");
                proType = cJSON_GetObjectItem(jsonPay, "productType");
                proCode = cJSON_GetObjectItem(jsonPay, "productCode");
                devCode = cJSON_GetObjectItem(jsonPay, "deviceCode");
                INFO_PRINT("productType productCode mac: [%s][%s] [%s] \n",proType->valuestring, proCode->valuestring, mac->valuestring);
                kk_mid_subdev_add(proType->valuestring,proCode->valuestring,devCode->valuestring, mac->valuestring);
                
        }else if (method != NULL && mac != NULL){
            INFO_PRINT("save property and send to cloud \n");
            kk_tsl_property_set_byMac(mac->valuestring, data, strlen(data)+1);
            
        }else{
            INFO_PRINT("kk_platMsg_handle data: don't handle it [%s]\n",data);
        
            //kk_tsl_service_property_set(topic->valuestring, payload->valuestring, strlen(payload->valuestring), NULL);
        }
        cJSON_Delete(json);
   }
}

void kk_platMsg_dispatch(void)
{
    int count = 0;
    void *data = NULL;

    while (CONFIG_DISPATCH_QUEUE_MAXLEN == 0 || count++ < CONFIG_DISPATCH_QUEUE_MAXLEN) {

        if (dm_queue_msg_next2(&data) == SUCCESS_RETURN) {
            dm_queue_msg_t *msg = (dm_queue_msg_t *)data;
            INFO_PRINT("kk_handle_platMsg_dispatch get call \n");
            if (kk_platMsg_handle) {
                kk_platMsg_handle(msg->data,msg->chalMark);
            }

            if (msg->data != NULL){
                free(msg->data);
            }
            free(data);
            data = NULL;
        } else {
            break;
        }
    }
}


time_t getSysTime(){
    time_t t;  
    t = time(NULL);  
    return t;

}

typedef struct {
    int auto_add_subdev;
    int master_devid;
    int cloud_connected;
    int master_initialized;
    int subdev_index;
    int permit_join;
    void *g_mid_dispatch_thread;
    void *g_ota_dispatch_thread;
    void *g_ccuProChg_dispatch_thread;	
	void *g_udp_dispatch_thread;
    int g_mid_dispatch_thread_running;
    int g_ota_dispatch_thread_running;
    int g_ccuProChg_dispatch_thread_running;	
	int g_udp_dispatch_thread_running;
} mid_ctx_t;
#define MID_YIELD_TIMEOUT_MS (200)


static mid_ctx_t g_mid_ctx;

static mid_ctx_t *kk_mid_get_ctx(void)
{
    return &g_mid_ctx;
}

extern void IOT_Linkkit_Yield(int timeout_ms);
void *mid_dispatch_yield(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();

    while (mid_ctx->g_mid_dispatch_thread_running) {
        IOT_Linkkit_Yield(MID_YIELD_TIMEOUT_MS);
    }

    return NULL;
}

void *ota_dispatch_yield(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();

    while (mid_ctx->g_ota_dispatch_thread_running) {
        dm_ota_yield(MID_YIELD_TIMEOUT_MS);
    }

    return NULL;
}


#define UDP_LAN_PORT 25556
#define UDP_LAN_PORT_HOST 25555

void *udp_dispatch_yield(void *args){  
    

    INFO_PRINT("udp_dispatch_yield udp thread create\n");    

    // 绑定地址 
    struct sockaddr_in addrto; 
    bzero(&addrto, sizeof(struct sockaddr_in)); 
    addrto.sin_family = AF_INET; 
    addrto.sin_addr.s_addr = htonl(INADDR_ANY); 
    addrto.sin_port = htons(UDP_LAN_PORT); 

     // 发送地址
    struct sockaddr_in addrto_host; 
    bzero(&addrto_host, sizeof(struct sockaddr_in)); 
    addrto_host.sin_family = AF_INET; 
    addrto_host.sin_addr.s_addr = htonl(INADDR_ANY); 
    //addrto_host.sin_port = htons(UDP_LAN_PORT); 
 
    // 接收到的广播地址 
    struct sockaddr_in from; 
    bzero(&from, sizeof(struct sockaddr_in)); 
   
    
    int sock = -1; 
     int sock_host = -1; 
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    { 
        ERROR_PRINT("socket error\n"); 
    } 

     if ((sock_host = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    { 
        ERROR_PRINT("socket error\n"); 
    } 
    
    if(bind(sock,(struct sockaddr *)&(addrto), sizeof(struct sockaddr_in)) == -1) 
    { 
        ERROR_PRINT("bind error...\n"); 
    } 
 
    socklen_t len = sizeof(struct sockaddr_in); 
    cJSON* json = NULL;
    cJSON* info = NULL;
    cJSON* payload = NULL;
    cJSON* infoObj = NULL;
    cJSON* payloadObj = NULL;
    cJSON* msgtype = NULL;
    cJSON* proType = NULL;
    cJSON* proCode = NULL;
    cJSON* devCode = NULL;
    cJSON* params = NULL;
    cJSON* macstr = NULL;
    char szOut[128] = {0};
    char szDec[1024] = {0}; 
    char host_ip[32] = {0};
    char mac[32] = {0};
    int devId = 0;

    while(1) 
    { 
            
        
        //从广播地址接受消息 
        memset(szDec, 0 , sizeof(szDec));
        int size=recvfrom(sock, szDec, sizeof(szDec), 0, (struct sockaddr*)&from,(socklen_t*)&len); 
        if(size<=0) 
        { 
            WARNING_PRINT("read error....\n"); 
        } 
        else 
        { 
                
            DEBUG_PRINT("lan recmsg: %s\n", szDec); 
            //DEBUG_PRINT("udp client ip:%s ,port is :%d htons(UDP_LAN_PORT)=%d \n",inet_ntoa(from.sin_addr),from.sin_port, htons(UDP_LAN_PORT)); 
            json=cJSON_Parse(szDec);
        	if (!json) {
                ERROR_PRINT("Error before: [%s]\n","cJSON_Parse");
            }
        	else
        	{
                
                infoObj = cJSON_GetObjectItem(json, "info");
                payloadObj = cJSON_GetObjectItem(json, "payload");
                
                if (infoObj != NULL && payloadObj != NULL){

                    msgtype = cJSON_GetObjectItem(infoObj, "msgtype");
                    if (msgtype == NULL || strstr(msgtype->valuestring, "/thing/topo/add") == NULL){
                        ERROR_PRINT("msgtype parameter is error \n");
                        cJSON_Delete(json);
                        continue;
                    }

                    proType = cJSON_GetObjectItem(infoObj, "productType");
                    proCode = cJSON_GetObjectItem(infoObj, "productCode");
                    devCode = cJSON_GetObjectItem(infoObj, "deviceCode");
                    params = cJSON_GetObjectItem(payloadObj, "params");
                    if (proType == NULL ||  proCode == NULL || devCode == NULL || params == NULL){
                        ERROR_PRINT("productType productCode deviceCode params parameters are error \n");
                        cJSON_Delete(json);
                        continue;
                    }
                    
                    macstr = cJSON_GetObjectItem(params, "mac");

                    if (macstr == NULL){
                        ERROR_PRINT("mac parameter is error \n");
                        cJSON_Delete(json);
                        continue;
                    }
                    
                    INFO_PRINT("productType productCode deviceCode mac: [%s][%s][%s][%s] \n",proType->valuestring, proCode->valuestring,
                                devCode->valuestring, macstr->valuestring);
                    int res = dm_mgr_gw_create(proType->valuestring,proCode->valuestring,devCode->valuestring,macstr->valuestring,&devId);
                	if (res != SUCCESS_RETURN) {
                        WARNING_PRINT("dm_mgr_gw_create error");
                	}
                    
                    kk_ipc_send(IPC_MID2APP, szDec, size);

                    memset(host_ip, 0, sizeof(host_ip));
                    memset(mac, 0, sizeof(mac));
                    memset(szOut, 0, sizeof(szOut));
                    HAL_Get_IP(host_ip,"ens33");
                    HAL_Get_mac(mac);
                    
                    sprintf(szOut,"/thing/topo/add_reply|mac=%s;ip=%s",mac,host_ip);
                    DEBUG_PRINT("szOut:%s\n",szOut); 
                    DEBUG_PRINT("udp client ip:%s ,port is :%d \n",inet_ntoa(from.sin_addr),from.sin_port); 
                    //sendto(sock, szOut, strlen(szOut), 0, (struct sockaddr*)&from,len); 

                    addrto_host.sin_addr.s_addr = inet_addr(inet_ntoa(from.sin_addr)); 
                    addrto_host.sin_port = htons(UDP_LAN_PORT_HOST); 
                    //addrto_host.sin_port = from.sin_port; 
                    if(strcmp(host_ip,inet_ntoa(from.sin_addr)) == 0)
                    {
                        sendto(sock_host, szOut, strlen(szOut), 0, (struct sockaddr*)&addrto_host,sizeof(addrto_host)); 
                    }
                    else
                    {
                        DEBUG_PRINT("udp client is not local ip  , refused send ack to it\n");
                    }
                    
                }else{
                    INFO_PRINT("error format json: [%s]\n",szDec);
                
                }
                
                cJSON_Delete(json);
                
            }

             
        }
        usleep(100000); 
    } 

            
    close(sock);
    close(sock_host);
}
void *ccu_property_monitor(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();
 	char s_IP[NETWORK_ADDR_LEN];
	char *s_IP_TSL = NULL;
	int res = 0;
	int needReport  = 0;
	static int s_cloudStatus = 0;
	int cloudState = 0;
	int time_second = 10;
	static int alreadyRepord = 0;
    while (mid_ctx->g_ccuProChg_dispatch_thread_running) {
        //dm_ota_yield(MID_YIELD_TIMEOUT_MS);
		HAL_Get_IP(s_IP,NULL);	
		res = kk_tsl_get_value(kk_tsl_get_property_value,0,KK_TSL_CCU_WANIP_IDENTIFIER,NULL,&s_IP_TSL);
		if(res != SUCCESS_RETURN){
			ERROR_PRINT("kk_tsl_get_value Failed\n");
		}
		else{
			if(strcmp(s_IP,s_IP_TSL)){
				kk_tsl_set_value(kk_tsl_set_property_value,0,KK_TSL_CCU_WANIP_IDENTIFIER,NULL,s_IP);
				INFO_PRINT("current ip:%s,db ip:%s\n",s_IP,s_IP_TSL);
				needReport  = 1;
			}
		}
		res = kk_tsl_get_value(kk_tsl_get_property_value,0,KK_TSL_CCU_IOTCLOUD_IDENTIFIER,&cloudState,NULL);
		
		
		if(res != SUCCESS_RETURN){
			ERROR_PRINT("kk_tsl_get_value Failed\n");
		}
		else{
			if(s_cloudStatus == cloudState){

			}else{
			   s_cloudStatus = cloudState;
			   INFO_PRINT("cloudState:%d,s_cloudStatus:%d\n",cloudState,s_cloudStatus);
			   needReport = 1;
			}
		}		
		if(alreadyRepord){
			time_second = 60;
		}
		else{
			time_second = 10;
		}
		if(needReport&&(cloudState == 1)){
			//kk_tsl_post_property(0,NULL);
			needReport = 0;
			if(alreadyRepord == 0){
				iotx_dm_dev_online(0);//first online,report the online status
				time_second = 60;
			}
			else{
				kk_tsl_post_property(0,NULL);
			}
			alreadyRepord = 1; 
		}
		INFO_PRINT("time_second:%d,s_cloudStatus:%d\n",time_second,s_cloudStatus);
		sleep(time_second);
    }

    return NULL;
}



static int kk_set_product_info(void)
{
	HAL_SetProduct_Type(PRODUCT_TPYE);
	HAL_SetProduct_Code(PRODUCT_CODE);	
	return 0;
}

int main(const int argc, const char **argv)
{
		
        int res = 0;
        mid_ctx_t *mid_ctx = kk_mid_get_ctx();
    
        kk_zlog_init("midware");
        memset(mid_ctx, 0, sizeof(mid_ctx_t));
		
		kk_set_product_info();
        kk_tsl_api_init();

        kk_ipc_init(IPC_MID2APP, mid_cb, NULL, NULL);
        kk_ipc_init(IPC_MID2PLAT, mid2p_cb, NULL, "*");
		


        //DB_Init();
        //test_tcp();
        /* when Connect to app and platfrom */
        /*do {
            res = IOT_Linkkit_Connect(user_example_ctx->master_devid);
            if (res < 0) {
                EXAMPLE_TRACE("IOT_Linkkit_Connect failed, retry after 5s...\n");
                HAL_SleepMs(5000);
            }
        } while (res < 0);*/
        kk_init_dmproc();
		kk_subDb_init();
        mid_ctx->g_mid_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_mid_dispatch_thread, NULL, mid_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate mid Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
        mid_ctx->g_ota_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_ota_dispatch_thread, NULL, ota_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate ota Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
        
        // recv gateway add cmd and ack to gateway
        res = pthread_create(&mid_ctx->g_udp_dispatch_thread, NULL, udp_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate udp Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
		
		mid_ctx->g_ccuProChg_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_ccuProChg_dispatch_thread, NULL, ccu_property_monitor, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate Failed\n");
            return -1;
        }		

        int ct = 0;
		for (;;) {
                usleep(200000);
                kk_platMsg_dispatch();
                /*if (ct == 0){
                    ct =1;
                    void* buf = "{        \"msgId\":        \"7\", \"version\":      \"1.0\", \"mac\":  \"588E81FFFED3834A\", \"method\":       \"thing.topo.add\",    \"params\":       {          \"AppVersion\":   \"10\",  \"deviceCode\":   \"588E81FFFED3834A\",   \"productType\":  \"curtain\",   \"productCode\":  \"24\"  }}";
                    kk_platMsg_handle(buf, "1122334455667788");
                    //kk_set_tsl_by_productKey("a1OYuSallan","model.json");
                    //kk_mid_subdev_add("a1OYuSallan","allanWno8yDdsjCX15iq","","aabbccddeeff1122");
                }*/

        }
}

