#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include "ip_alarm.h"

#define IP_ALARM_APP_KEY "5MHgJo6T3eMcvgIu"
#define VIDEO_TIME 60

static int get_rt_data_from_json_str(pp_real_time_data_t *real_data, char *json_str)
{
    int ret = 0;

    cJSON *root = cJSON_Parse(json_str);
    if (root)
    {
        char *payload = NULL;
        char *buf = NULL;

        buf = (char *)malloc(MAXBUF);
        if (buf == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed");
            ret = -1;
            goto out;
        }

        GET_JSON_VALUE_STRING(root, "port", buf);
        real_data->port = port_char2enum(buf);
        GET_JSON_VALUE_INT(root, "instruction_code", real_data->instruction_code);
        GET_JSON_VALUE_INT(root, "mi", real_data->mi);
        GET_JSON_VALUE_INT(root, "ts", real_data->ts);
        GET_JSON_VALUE_INT(root, "len", real_data->len);
        GET_JSON_VALUE_INT(root, "sub_term_addr_len", real_data->sub_term_addr_len);
        GET_JSON_VALUE_INT(root, "start_addr", real_data->start_addr);
        GET_JSON_VALUE_STRING(root, "sub_template_id", real_data->sub_template_id);
        GET_JSON_VALUE_STRING(root, "src_identifier", real_data->src_identifier);
        GET_JSON_VALUE_STRING(root, "sn", real_data->sn);
        GET_JSON_VALUE_STRING(root, "dtu_sn", real_data->dtu_sn);

        GET_JSON_VALUE_STRING(root, "data_b64", buf);
		dy_syslog(LOG_DEBUG, "buf:%s, real_data->len:%d", buf, real_data->len);
        real_data->data = calloc(real_data->len+1, 1);
        if (real_data->data == NULL)
        {
            dy_syslog(LOG_ERR, "malloc fail len:%d", real_data->len);
            free(buf);
            goto out;
        }
        memcpy(real_data->data, buf, real_data->len);
		dy_syslog(LOG_DEBUG, "real_data->data:%s, real_data->len:%d", real_data->data, real_data->len);
		free(buf);
    }
    else
    {
        dy_syslog(LOG_ERR, "json parse error");
        ret = -1;
    }

out:
    cJSON_Delete(root);

    return ret;
}

static int get_ffmpeg_status(char *cmd, char *ip_add)
{
    int ret = 0;
    char *string = NULL;
	
    ret = system_reply(cmd, &string);
	dy_syslog(LOG_DEBUG, "cmd %s ret %d string %p", cmd, ret, string);
    if (ret == 0)
    {
        if (!string)return 0;
		dy_syslog(LOG_DEBUG, "string:%s %d", string, strlen(string));
		if(strstr(string, ip_add) && strstr(string, "-f mp4"))
			ret = 1;
        free(string);
		return ret;
    }
    return 0;
}

static int ipc_push_video_streaming_check(ip_alarm_var_t *var, char *ip_add)
{
    int ret = 0;

	ret = get_ffmpeg_status("pgrep -fl ffmpeg", ip_add);
	dy_syslog(LOG_DEBUG, "get_ffmpeg_status %d", ret);
	if(ret == 0)
	{
		ret = get_ffmpeg_status("pgrep -afl ffmpeg", ip_add);
		dy_syslog(LOG_DEBUG, "get_ffmpeg_status %d", ret);
	}

	return ret;
}

void push_streaming(ip_alarm_var_t *var, ip_alarm_config_t *ip_alarm_cfg)
{
	char cmd[128] = {0};
	snprintf(cmd, sizeof(cmd), "rm /tmp/%s", ip_alarm_cfg->video_file);
	dy_syslog(LOG_DEBUG, "cmd:%s", cmd);
	system(cmd);
	
	if(strlen(ip_alarm_cfg->record))
	{
		dy_syslog(LOG_DEBUG, "push_streaming record:%s", ip_alarm_cfg->record);
		system(ip_alarm_cfg->record);
	}
}

static void ipc_data_reply(ip_alarm_var_t *var, pp_real_time_data_t *real_data, char *result_data, int len)
{
	tag_table_t ptag = {0};
	tag_table_t ptag_post = {0};
					
	strcpy(ptag.sn, real_data->sn);
	ptag.mi = real_data->mi;		
	strncpy(ptag.identifier, real_data->src_identifier, strlen(real_data->src_identifier));
	ptag.report_period = 0;
	ptag.data_type = DATA_TYPE_SERVICE;
	ptag.tag_node = calloc((len)+1, 1);
	memcpy(ptag.tag_node, result_data, len);
	ptag.time = time(NULL);

	memcpy(&ptag_post, &ptag, sizeof(tag_table_t));
	ptag_post.data_type = DATA_TYPE_EVENT;
	ptag_post.tag_node = calloc((len)+1, 1);
	memcpy(ptag_post.tag_node, result_data, len);

	dy_syslog(LOG_DEBUG, "parse result_data:%s post:%s", ptag.tag_node, ptag_post.tag_node);
    if (ptag.tag_node != NULL)
    {
		ptag.port = real_data->port;
		strncpy(ptag.app_key, IP_ALARM_APP_KEY, MAX_APP_KEY_LEN);
        pp_send_tag_data(var->session, &ptag);
        free(ptag.tag_node);
    }
    if (ptag_post.tag_node != NULL)
    {
		ptag_post.port = real_data->port;
		strncpy(ptag_post.app_key, IP_ALARM_APP_KEY, MAX_APP_KEY_LEN);
        pp_send_tag_data(var->session, &ptag_post);
        free(ptag_post.tag_node);
    }
}

static void ip_alarm_status_update(ip_alarm_var_t *var, ip_alarm_config_t *ip_alarm_cfg, int online)
{
	ip_alarm_cfg->online = online;
	
	cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        return;
    }

    cJSON *nodes = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "status", nodes);
    cJSON *item = cJSON_CreateObject();
    cJSON_AddItemToArray(nodes, item);
    char buff[100];
    if (item)
    {
        cJSON_AddStringToObject(item, "sn", ip_alarm_cfg->camera_cfg.sn);
		cJSON_AddBoolToObject(item, "online", online);	
		cJSON_AddNumberToObject(item, "last_rcv", time(NULL));
		cJSON_AddNumberToObject(item, "login_time", 0);
    }

	char *json_str = cJSON_PrintUnformatted(root);

	char topic[256] = {0};
	snprintf(topic, sizeof(topic), "ipc/%s/TCP_CLIENT/Evt_NodesStatus", var->sn_str);

	ipc_session_publish(var->session, topic, json_str, strlen(json_str));
	dy_syslog(LOG_DEBUG, "ip_alarm topic:%s payload:%s", topic, json_str);

	free(json_str);
	cJSON_Delete(root);
}

static int ip_alarm_heart_beat(ip_alarm_var_t *var)
{
	TIMER_CONFIRM(var->heartbeat_timer);
	int ret = -1;
	char exec_cmd[256] = {0};
    char *buff = NULL;

    sprintf(exec_cmd, "%s%s status", NET_BASH_PATH, "fetch_dhcp_mac_status.sh");
    system_reply(exec_cmd, &buff);
    if (!buff)
    {
        dy_syslog(LOG_ERR, "system run \'%s\' fail !", exec_cmd);
        goto _out;
    }

    cJSON *root = cJSON_Parse(buff);
    if (!root)
    {
		goto _out;
    }

	cJSON *status = cJSON_GetObjectItem(root, "status");
    if (!status)
    {
        dy_syslog(LOG_ERR, "get status failed");
        goto _out;
    }

    int node_cnt = cJSON_GetArraySize(status);
	int i;
	cJSON *node  = NULL;
	char ip[32] = {0};
	char mac[32] = {0};

    for (i = 0; i < node_cnt ; i++)
    {
        node = cJSON_GetArrayItem(status, i);
        if (node)
        {
            GET_JSON_VALUE_STRING(node, "ip", ip);
            GET_JSON_VALUE_STRING(node, "mac", mac);

			ip_alarm_config_t *ip_alarm_cfg = NULL;
			list_for_each_entry(ip_alarm_cfg, &var->config_list, list)
			{
				//检索ipc设备
				if(strcmp(ip_alarm_cfg->camera_cfg.ip_addr, ip) == 0)
				{
					dy_syslog(LOG_DEBUG, "%s %d sn:%s ip_addr:%s", __FUNCTION__,__LINE__,ip_alarm_cfg->camera_cfg.sn,ip_alarm_cfg->camera_cfg.ip_addr);
					if(strcmp("00:00:00:00:00:00", mac))
					{
						if(ip_alarm_cfg->online == false)
						{
							ip_alarm_status_update(var, ip_alarm_cfg, 1);
						}
					}
					else if(ip_alarm_cfg->online == true)
					{
						ip_alarm_status_update(var, ip_alarm_cfg, 0);
					}
					break;
				}
			}
        }
	}

	ret = 0;
_out:
	if(buff)
		free(buff);
	if(root)
		cJSON_Delete(root);
	return ret;
}

static int ip_alarm_push_video_timer(ip_alarm_var_t *var)
{
    int ret = 1;
	time_t now = time(NULL);

	TIMER_CONFIRM(var->push_video_timer);
	#if 1
	ip_alarm_config_t *ip_alarm_cfg = NULL;
	list_for_each_entry(ip_alarm_cfg, &var->config_list, list)
	{
		//守护录像
		if(ip_alarm_cfg->push_video_flag && now - ip_alarm_cfg->last_push < 10 && ipc_push_video_streaming_check(var, ip_alarm_cfg->camera_cfg.ip_addr) == 0)
		{
			push_streaming(var, ip_alarm_cfg);
		}
		//录像完成
		if(ip_alarm_cfg->push_video_flag && now >= ip_alarm_cfg->last_push + ip_alarm_cfg->camera_cfg.video_time && ipc_push_video_streaming_check(var, ip_alarm_cfg->camera_cfg.ip_addr) == 0)
		{
			ip_alarm_cfg->push_video_flag = 0;
			pp_real_time_data_t real_data = {0};

			strcpy(real_data.sn, ip_alarm_cfg->camera_cfg.sn);
			//ipc_stop_video_streaming(var);
			#if 1
			char push_buf[256] = {0};
			snprintf(push_buf, 256, "scp /tmp/%s guest@repository.lnxall.com:/opt/wookong/repository/video_file/", ip_alarm_cfg->video_file);//root@118.178.126.254:/opt/wookong/repository/video_file/
			//usleep(500000);
			system(push_buf);
			//system_reply(push_buf, NULL);
			dy_syslog(LOG_DEBUG, "push_buf:%s", push_buf);
			//sleep(1);
			snprintf(push_buf, 256, "http://repository.lnxall.com/repository/video_file/%s", ip_alarm_cfg->video_file);//http://118.178.126.254/repository/video_file
			
			strcpy(real_data.src_identifier, "UploadVideoFiles");
			
			cJSON *jtag = cJSON_CreateObject();
			cJSON_AddStringToObject(jtag, "url", push_buf);
			char *tag = cJSON_PrintUnformatted(jtag);			
			ipc_data_reply(var, &real_data, tag, strlen(tag));
			
			//system("rm /tmp/*.mp4");
			#endif
		}		
	}
	#endif
    return 0;
}


static void ip_alarm_loop(ip_alarm_var_t *var)
{
    int ret = -1, maxfd;
    fd_set  rset;
    struct timeval timeout;

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->heartbeat_timer);
		SELECT_ADD_FD(var->push_video_timer);
        timeout.tv_usec = 0;
        timeout.tv_sec = 10;
        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            printf("errno %d\n", errno);
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret == 0)
        {
        }
        else
        {
            if (var->heartbeat_timer > 0 && FD_ISSET(var->heartbeat_timer, &rset))
            {
                FD_CLR(var->heartbeat_timer, &rset);
                ip_alarm_heart_beat(var);
            }
			if (var->push_video_timer > 0 && FD_ISSET(var->push_video_timer, &rset))
            {
                FD_CLR(var->push_video_timer, &rset);
                ip_alarm_push_video_timer(var);
            }
        }
    }
}

static void ip_alarm_subscribe_all(ip_alarm_var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/device/+/data/%s", IP_ALARM_APP_KEY, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA);
    ipc_session_subscribe(ipc_session, topic);
}

void ip_alarm_msg_ctrl(ip_alarm_var_t *var, pp_real_time_data_t *real_data)
{
	int find = 0;
	int ret;	
	ip_alarm_config_t *ip_alarm_cfg = NULL;

	list_for_each_entry(ip_alarm_cfg, &var->config_list, list)
	{
		//检索ipc设备
		dy_syslog(LOG_DEBUG, "%s %d sn:%s %s", __FUNCTION__,__LINE__,real_data->sn, ip_alarm_cfg->camera_cfg.sn);
		if(strcmp(real_data->sn, ip_alarm_cfg->camera_cfg.sn) == 0)
		{			
			dy_syslog(LOG_DEBUG, "%s %d src_identifier:%s", __FUNCTION__,__LINE__,real_data->src_identifier);
			if(strcmp(real_data->src_identifier, "ring") == 0 || strcmp(real_data->src_identifier, "handup") == 0)
			{
				if(ip_alarm_cfg->online == false)
				{
					ip_alarm_status_update(var, ip_alarm_cfg, 1);
				}
				
				pp_real_time_data_t real_data_tmp = {0};
				strcpy(real_data_tmp.sn, ip_alarm_cfg->camera_cfg.sn);
				strcpy(real_data_tmp.src_identifier, "StatusUpdate");
				
				cJSON *jtag = cJSON_CreateObject();
				cJSON_AddStringToObject(jtag, "status", real_data->src_identifier);
				char *tag = cJSON_PrintUnformatted(jtag);			
				ipc_data_reply(var, &real_data_tmp, tag, strlen(tag));
				
				free(tag);
				cJSON_Delete(jtag);

				if(strstr(real_data->src_identifier, "ring"))
				{
					time_t now = time(NULL);
					if(now - ip_alarm_cfg->last_push > 60)
					{
						ip_alarm_cfg->last_push = now;
						snprintf(ip_alarm_cfg->video_file, sizeof(ip_alarm_cfg->video_file), "%d.mp4", ip_alarm_cfg->last_push);
						snprintf(ip_alarm_cfg->record, 512, "ffmpeg -rtsp_transport tcp -nostdin -i \"%s\" -an -t %d -vcodec copy -f mp4 /tmp/%s 2>&1 | logger -t 'ffmpeg' & ", ip_alarm_cfg->camera_cfg.rtsp_url,ip_alarm_cfg->camera_cfg.video_time,ip_alarm_cfg->video_file);

						if(ip_alarm_cfg->record && strlen(ip_alarm_cfg->record))
						{
							dy_syslog(LOG_DEBUG, "record:%s", ip_alarm_cfg->record);
							system(ip_alarm_cfg->record);
						}
						ip_alarm_cfg->push_video_flag = 1;
					}
				}
			}
			else if(strcmp(real_data->src_identifier, "online") == 0)
			{
				ip_alarm_status_update(var, ip_alarm_cfg, 1);
			}
			else if(strcmp(real_data->src_identifier, "offline") == 0)
			{
				ip_alarm_status_update(var, ip_alarm_cfg, 0);
			}
			break;
		}
	}
}

static int ip_alarm_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    ip_alarm_var_t *var = (ip_alarm_var_t *)obj;

    dy_syslog(LOG_DEBUG, " MQTT client: received MQTT topic:%s payload length:%d",
              mqtt_msg->topic, mqtt_msg->payloadLen);
	dy_syslog(LOG_INFO, "topic %s RAW_DATA:%s", mqtt_msg->topic, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA);
    if (strstr(mqtt_msg->topic, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA))
    {
    	int ret = 0;
    	pp_real_time_data_t real_data = {0};
		
		dy_syslog(LOG_INFO, "111111");
	    ret = get_rt_data_from_json_str(&real_data, mqtt_msg->payload);
	    if (ret < 0)
	    {
	        dy_syslog(LOG_ERR, "parse real data structure failed");
	    }
		dy_syslog(LOG_INFO, "2222222");
		dy_syslog(LOG_INFO, "data %s len:%d", real_data.data,real_data.len);

		ip_alarm_msg_ctrl(var, &real_data);
		
		if (real_data.data)
	    {
	        free(real_data.data);
	    }
    }
}

// 建立与内部broker之间的MQTT连接
static int ip_alarm_mqtt_client_init(ip_alarm_var_t *var)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};

    snprintf(clientId, MAX_CLIENT_ID_LEN, "%s", var->proc_name);
    var->session = ipc_session_new(clientId, (void*)var, IPC_DEFAULT);
    if (var->session == NULL) return -1;

    ipc_session_set_callbacks(var->session, ip_alarm_mqtt_handle_recv_msg, NULL);
    ip_alarm_subscribe_all(var);
    ipc_session_start(var->session);
}

static int ip_alarm_init(ip_alarm_var_t *var)
{
	int i;
	int start = 0;
	
	INIT_LIST_HEAD(&var->config_list);
	INIT_LIST_HEAD(&var->data_list);
	
    get_board_sn(var->sn_str);
    get_process_name(var->proc_name);

	system("killall -15 ffmpeg");
	system("killall -15 ffmpeg");
	system("rm /tmp/*.mp4");

    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
    }
	if (load_templates_cfg(&var->template_table, TEMPLATES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load template cfg fail");
    }

	ip_alarm_mqtt_client_init(var);
#if 1
	for(i=0; i<var->nodes_cfg_table->node_cnt; i++)
	{
		if(strcmp(var->nodes_cfg_table->node[i].app_key, IP_ALARM_APP_KEY) == 0 || (var->nodes_cfg_table->node[i].ext_data != NULL && strstr(var->nodes_cfg_table->node[i].ext_data, IP_ALARM_APP_KEY)))
		{
			ip_alarm_config_t *ip_alarm_cfg = calloc(1, sizeof(ip_alarm_config_t));
			ip_alarm_cfg->camera_cfg.port = 554;

			ip_alarm_cfg->camera_cfg.rtsp_url = calloc(512, 1);
			ip_alarm_cfg->camera_cfg.rtsp_url_local = calloc(512, 1);
			
			cJSON *root = cJSON_Parse(var->nodes_cfg_table->node[i].ext_data);
		    if (root)
		    {
				GET_JSON_VALUE_STRING(root, "user", ip_alarm_cfg->camera_cfg.user);
				GET_JSON_VALUE_STRING(root, "password", ip_alarm_cfg->camera_cfg.password);
				GET_JSON_VALUE_INT(root, "video_time", ip_alarm_cfg->camera_cfg.video_time);
				GET_JSON_VALUE_STRING(root, "rtsp_url", ip_alarm_cfg->camera_cfg.rtsp_url);
				cJSON_Delete(root);
		    }

			if(ip_alarm_cfg->camera_cfg.video_time == 0 || ip_alarm_cfg->camera_cfg.video_time <= 5)
				ip_alarm_cfg->camera_cfg.video_time = VIDEO_TIME;
			
			strncpy(ip_alarm_cfg->camera_cfg.sn, var->nodes_cfg_table->node[i].sn, sizeof(ip_alarm_cfg->camera_cfg.sn));
			strncpy(ip_alarm_cfg->camera_cfg.ip_addr, var->nodes_cfg_table->node[i].tcp_ip_addr, sizeof(ip_alarm_cfg->camera_cfg.ip_addr));
			if(strlen(ip_alarm_cfg->camera_cfg.user) == 0 || strlen(ip_alarm_cfg->camera_cfg.password) == 0)
			{
				strcpy(ip_alarm_cfg->camera_cfg.user, "admin");
				strcpy(ip_alarm_cfg->camera_cfg.password, "tlJwpbo6");
			}

			if(strlen(ip_alarm_cfg->camera_cfg.rtsp_url) == 0)
			{
				snprintf(ip_alarm_cfg->camera_cfg.rtsp_url, 512, "rtsp://%s:%d/user=%s&password=%s&channel=1&stream=1.sdp?real_stream",ip_alarm_cfg->camera_cfg.ip_addr,ip_alarm_cfg->camera_cfg.port,ip_alarm_cfg->camera_cfg.user,ip_alarm_cfg->camera_cfg.password);
				snprintf(ip_alarm_cfg->camera_cfg.rtsp_url_local, 512, "rtsp://%s:%d/user=%s&password=%s&channel=1&stream=1.sdp?real_stream",ip_alarm_cfg->camera_cfg.ip_addr,ip_alarm_cfg->camera_cfg.port,ip_alarm_cfg->camera_cfg.user,ip_alarm_cfg->camera_cfg.password);
			}

			dy_syslog(LOG_INFO,"sn:%s video_time:%d rtsp_url:%s",ip_alarm_cfg->camera_cfg.sn,ip_alarm_cfg->camera_cfg.video_time,ip_alarm_cfg->camera_cfg.rtsp_url);
			dy_syslog(LOG_INFO,"==sn:%s ip_addr:%s port:%d user:%s password:%s==",ip_alarm_cfg->camera_cfg.sn,ip_alarm_cfg->camera_cfg.ip_addr,ip_alarm_cfg->camera_cfg.port,ip_alarm_cfg->camera_cfg.user,ip_alarm_cfg->camera_cfg.password);
			list_add_tail(&ip_alarm_cfg->list, &var->config_list);
		}
	}
#endif
    var->heartbeat_timer = my_timer_create();
    if (var->heartbeat_timer > 0)
    {
    	my_timer_set(var->heartbeat_timer, 3, 300000);
    }

	var->push_video_timer = my_timer_create();
    if (var->push_video_timer > 0)
    {
    	my_timer_set(var->push_video_timer, 3, 1000);
    }

	//update_depend_package_info(var);

    dy_syslog(LOG_INFO, "init done, board SN:%s", var->sn_str);

    return 0;
}

int main(int argc, char *argv[])
{
    ip_alarm_var_t var;

    memset(&var, 0, sizeof(ip_alarm_var_t));

    ip_alarm_init(&var);	
    ip_alarm_loop(&var);

    return 0;
}
