#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

#include "dy_utils/dy_common.h"
#include "cloud_iec104_common.h"
#include "dy_utils/cJSON.h"
#include "dy_utils/protocol.h"

#define IEC104_STR		"IEC104_"

/* Callback handler to log sent or received messages (optional) */
static void rawMessageHandler (void* parameter, uint8_t* msg, int msgSize, bool sent)
{
	channel_t *channel = (channel_t *)parameter;
    if (sent)
    {
        dy_syslog_hex(LOG_INFO, msg, msgSize, "len %d			网关->[%s:%d]		", msgSize, channel->addr, channel->port);
    }
    else
        dy_syslog_hex(LOG_INFO, msg, msgSize, "len %d			网关->[%s:%d]		", msgSize, channel->addr, channel->port);
}

/* Connection event handler */
static void connectionHandler (void* parameter, CS104_Connection connection, CS104_ConnectionEvent event)
{
	channel_t *channel = (channel_t *)parameter;
    switch (event) {
    case CS104_CONNECTION_OPENED:
		channel->connect_status = 1;
        dy_syslog(LOG_INFO,"Connection established [%s:%d]", channel->addr, channel->port);
		CS104_Connection_sendStartDT(channel->con);
        break;
    case CS104_CONNECTION_CLOSED:
		channel->connect_status = 2;
        dy_syslog(LOG_INFO,"Connection closed [%s:%d]", channel->addr, channel->port);
		//CS104_Connection_connect(channel->con);
		system("/etc/init.d/cloud_conn_iec104 restart");
        break;
    case CS104_CONNECTION_STARTDT_CON_RECEIVED:
        dy_syslog(LOG_INFO,"Received STARTDT_CON");
        break;
    case CS104_CONNECTION_STOPDT_CON_RECEIVED:
        dy_syslog(LOG_INFO,"Received STOPDT_CON");
        break;
    }
}

/*
 * CS101_ASDUReceivedHandler implementation
 *
 * For CS104 the address parameter has to be ignored
 */
static bool asduReceivedHandler (void* parameter, int address, CS101_ASDU asdu)
{
    dy_syslog(LOG_INFO,"RECVD ASDU type: %s(%i) elements: %i",
            TypeID_toString(CS101_ASDU_getTypeID(asdu)),
            CS101_ASDU_getTypeID(asdu),
            CS101_ASDU_getNumberOfElements(asdu));

    if (CS101_ASDU_getTypeID(asdu) == M_ME_TE_1) {

        dy_syslog(LOG_INFO,"  measured scaled values with CP56Time2a timestamp:");

        int i;

        for (i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) {

            MeasuredValueScaledWithCP56Time2a io =
                    (MeasuredValueScaledWithCP56Time2a) CS101_ASDU_getElement(asdu, i);

            dy_syslog(LOG_INFO,"    IOA: %i value: %i",
                    InformationObject_getObjectAddress((InformationObject) io),
                    MeasuredValueScaled_getValue((MeasuredValueScaled) io)
            );

            MeasuredValueScaledWithCP56Time2a_destroy(io);
        }
    }
    else if (CS101_ASDU_getTypeID(asdu) == M_SP_NA_1) {
        dy_syslog(LOG_INFO,"  single point information:");

        int i;

        for (i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) {

            SinglePointInformation io =
                    (SinglePointInformation) CS101_ASDU_getElement(asdu, i);

            dy_syslog(LOG_INFO,"    IOA: %i value: %i",
                    InformationObject_getObjectAddress((InformationObject) io),
                    SinglePointInformation_getValue((SinglePointInformation) io)
            );

            SinglePointInformation_destroy(io);
        }
    }
    else if (CS101_ASDU_getTypeID(asdu) == C_TS_TA_1) {
        dy_syslog(LOG_INFO,"  test command with timestamp");
    }

    return true;
}

int recv_cloud_iec104_msg(channel_t *channel, mqtt_message_t *mqtt_msg)
{
    char topic[TOPIC_MAX_LEN] = {0};
    unsigned char matched;
    int j = 0, i = 0;
    int ret;
    cloud_mqtt_var_t *var = channel->var;

    dy_syslog(LOG_DEBUG, "External MQTT client received MQTT topic:%s payload:%s", mqtt_msg->topic, mqtt_msg->payload);

    for (j = 0; j < channel->topic_maps.downlink_cnt; j++)
    {
        ret = mqtt_topic_matches_sub(channel->topic_maps.downlink[j].external_topic, mqtt_msg->topic, &matched);
        if (ret == 0 && matched)
        {

            if (strstr(channel->topic_maps.downlink[j].internal_topic, "[PORT]") != NULL ||
                    strstr(channel->topic_maps.downlink[j].internal_topic, "[DEV_SN]") != NULL ||
                    strstr(channel->topic_maps.downlink[j].internal_topic, "[SID]") != NULL)
            {
                char port[64] = {0};
                char sn[SN_MAX_LEN] = {0};
                char identifier[64] = {0};
                char topic_2[TOPIC_MAX_LEN] = {0};
                cJSON *node = cJSON_Parse(mqtt_msg->payload);
                if (!node)
                {
                    dy_syslog(LOG_WARNING, "json rsData %s error!!!", mqtt_msg->payload);
                    continue;
                }
                GET_JSON_VALUE_STRING(node, "sn", sn);
                GET_JSON_VALUE_STRING(node, "identifier", identifier);
                replace_sub_str(channel->topic_maps.downlink[j].internal_topic, "[DEV_SN]", strlen(sn) == 0 ? "UNKNOWN_SN" : sn, topic);

                GET_JSON_VALUE_STRING(node, "port", port);
                if (strlen(sn) != 0 && strlen(port) == 0)
                {
                    // find port according SN
                    //遍历设备列表
                    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
                    {
                        if (strcmp(sn, var->nodes_cfg_table->node[i].sn) == 0)
                        {
                            if (strlen(var->nodes_cfg_table->node[i].app_key) != 0)
                            {
                                strcpy(port, var->nodes_cfg_table->node[i].app_key);
                            }
                            else
                            {
                                strcpy(port, port_enum2char(var->nodes_cfg_table->node[i].port));
                            }
                            break;
                        }
                    }
                }

                replace_sub_str(topic, "[PORT]", strlen(port) == 0 ? "UNKNOWN_PORT" : port, topic_2);
                replace_sub_str(topic_2, "[SID]", strlen(identifier) == 0 ? "UNKNOWN_SID" : identifier, topic);
                dy_syslog(LOG_DEBUG, "forward to internal topic:%s", topic);
                ipc_session_publish(var->session, topic, mqtt_msg->payload, mqtt_msg->payloadLen);
                cJSON_Delete(node);
            }
            else
            {
                dy_syslog(LOG_DEBUG, "forward to internal topic:%s", channel->topic_maps.downlink[j].internal_topic);
                ipc_session_publish(var->session, channel->topic_maps.downlink[j].internal_topic,
                                        mqtt_msg->payload, mqtt_msg->payloadLen);
            }
        }
    }

out:
    return 0;
}

int iec104_message_produce(channel_t *channel, char *topic, char *payload, int payloadLen)
{
	dy_syslog(LOG_DEBUG, "topic:%s payloadLen %d payload %s", topic,payloadLen,payload);
	int ret = 0;
	cJSON *root = cJSON_Parse(payload);
	if(root)
	{
		//char time_str[16] = {0};
		char sn[64] = {0};
		//time_t time = 0;

		GET_JSON_VALUE_STRING(root, "sn", sn);
		cJSON *tags = cJSON_GetObjectItem(root, "tags");
        if (tags)
        {
        	CS101_AppLayerParameters alParams = CS104_Connection_getAppLayerParameters(channel->con);
			CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, alParams->originatorAddress, false, false);
			InformationObject io = NULL;
            cJSON *child = NULL;
		    for (child = tags->child; child; child = child->next)
		    {
		    	if (child->string)
		        {
		        	char tag[72] = {0};
					char tagv[16] = {0};
		        	char *tag_s = strstr(child->string, IEC104_STR);
					if(tag_s && strlen(tag_s) > strlen(IEC104_STR))
					{
						uint16_t info_addr;
						tag_s += strlen(IEC104_STR);
						sscanf(tag_s, "%d", &info_addr);
						dy_syslog(LOG_DEBUG, "tag:%s val %1.15g io %p info_addr:%d", child->string,child->valuedouble, io, info_addr);

						if(io == NULL)
						{
							io = (InformationObject) MeasuredValueShort_create(NULL, info_addr, child->valuedouble, IEC60870_QUALITY_GOOD);//MeasuredValueShortWithCP56Time2a_create
							CS101_ASDU_addInformationObject(newAsdu, io);
						}
						else
						{
							CS101_ASDU_addInformationObject(newAsdu, (InformationObject)MeasuredValueShort_create((MeasuredValueShort) io, info_addr, child->valuedouble, IEC60870_QUALITY_GOOD));
						}
					}		            
		        }
		    }
			if(io != NULL)
			{
	        	InformationObject_destroy(io);
				CS104_Connection_sendASDU(channel->con, newAsdu);
			}
			CS101_ASDU_destroy(newAsdu);
        }
	}

	//iec104_message_produce_handle(channel, iec104_topic, iec104_payload, strlen(iec104_payload));
_out:
	return ret;
}

// 建立与broker之间的MQTT连接
int cloud_iec104_channel_create(cloud_mqtt_var_t *var)
{
    int i = 0, j = 0;
    char tmp[128] = {0};
    const char *board_name = NULL;
    board_name = get_board_name();

	if(var->iec104_channel_cnt <= 0)
	{
		dy_syslog(LOG_DEBUG, "iec104_channel_cnt:%d", var->iec104_channel_cnt);
		return 0;
	}
	
    for (i = 0; i < var->iec104_channel_cnt; i++)
    {
        channel_t *channel = &var->iec104_channel[i];
        char db_name[32] = {0};

		dy_syslog(LOG_DEBUG, "channel:%p board_name:%s", channel,board_name);
		dy_syslog(LOG_DEBUG, "resume:%d", channel->resume);

        channel->var = var;

        if (channel->resume)
        {
        	dy_syslog(LOG_DEBUG, "resume:%d", channel->resume);
            if (strcmp(board_name, BOARD_WOOLINK_MT7628) == 0)
            {
                sprintf(db_name, "/tmp/iec104_REPORT_%d.db", i);
            }
            else
            {
                sprintf(db_name, "/app/iec104_REPORT_%d.db", i);
            }
			dy_syslog(LOG_DEBUG, "db_name:%s", db_name);
            dy_db_session_init(&channel->db_session, db_name, (void*)channel);
        }
		dy_syslog(LOG_DEBUG, "resume:%d addr:%s port:%d asdu_addr:%d", channel->resume, channel->addr, channel->port, channel->asdu_addr);
		channel->con = CS104_Connection_create(channel->addr, channel->port);

	    CS101_AppLayerParameters alParams = CS104_Connection_getAppLayerParameters(channel->con);
	    alParams->originatorAddress = channel->asdu_addr;

	    CS104_Connection_setConnectionHandler(channel->con, connectionHandler, (void*)channel);
	    CS104_Connection_setASDUReceivedHandler(channel->con, asduReceivedHandler, (void*)channel);

	    /* uncomment to log messages */
	    CS104_Connection_setRawMessageHandler(channel->con, rawMessageHandler, (void*)channel);
		CS104_Connection_connect(channel->con);
    }
}
