#include <sys/poll.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <spawn.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>
#include <pthread.h>
#include "hiredis/hiredis.h"
#include "cjson/cJSON.h"
#include "iniparser/iniparser.h"
#include "log.h"

#define Boolean int
#define TRUE 1
#define FALSE 0
#define MAX_PIPE_BUFSIZE    256

//KEYS
#define UNKEY   0
#define KEY     1
#define KEY2_3  2
#define KEY2_10 3
#define OPEN    4
#define CLOSED   4

//SCHEDULE STATE
#define ERROR	-1
#define STOP	0
#define RUNNING	1
#define PAUSED  2

#define MAX_PRIORITY    10
#define MAX_TRY         3

//BARESIP_STATUS
#define IDLE        0
#define INUSE       1
#define RING        2
#define RINGING     3
#define HOLD        4
#define OFFLINE     5
#define DIALING     6
//BARESIP_REG
#define REGISTER_FAIL   0
#define REGISTER_OK     1
//LED_STATUS
#define Led_Off     0
#define Led_On      1
#define Led_Fast    2
#define Led_Slow    3
//REDIS_CHANNEL
#define BARESIP_CHAN    0
#define GPIO_CHAN       1
//GPIO_STATUS
#define GPIO_OFF    0
#define GPIO_ON     1

#define SPK_CONF    "/etc/speaker.conf"
#define VOL_CONF    "/oem/etc/volctrl.conf"
#define SIPLOGPATH  "/userdata/system.csv"
#define CDR_CSV		"/userdata/cdr.csv"
#define SCHEDULE_CONF	"/oem/etc/schedule.conf"
#define unix_socket_path "/tmp/redis.sock"

//operator log type
#define BTN			"BUTTON"
#define FUN			"FUNCTION"
#define STA         "SIP STATE"
#define SCHED		"SCHEDULE"

#define GPIO_RELAY   "gpio112"
#define VAR_IP      "${ip}"
#define VAR_MAC     "${mac}"
#define VAR_UA      "${ua}"
#define VAR_NUM     "${number}"

#define AUDIO_ON     "{\"name\": \"SCHEDULER-AUDIO\", \"action\": \"on\"}"
#define AUDIO_OFF    "{\"name\": \"SCHEDULER-AUDIO\", \"action\": \"off\"}"

struct date{              //声明结构体date用于表示日期
    int year;
    int month;
    int day;
};

struct VLC_INFO{
	pid_t pid;
    int prio;
	char param[96];
};

typedef struct _playInfo{
    int key_id;
    int prio;
    int vol;
	char file[64];
	char times_str[8];
	char enBeep[8];
    char enSrceen[8];
} PlayInfo;

struct CALL_LOG{
	char date[16];
	char start[16];
	char end[16];
	char acc[32];
	char num[32];
	char direct[8];
	char status[16];
	time_t answerTime;
	int duration;
	char desc[64];
};

typedef struct {
    int in_use[5];
    redisContext *context[5];
    const char *socket_path;
    time_t last_activity[5];
    pthread_mutex_t lock;
} redis_client_t;

static int BARESIP_STATUS = OFFLINE;
static int CURRENT_KEY = UNKEY;
static int DEBUG = FALSE;
static int ACCOUNT1_REG = REGISTER_FAIL;
static int ACCOUNT2_REG = REGISTER_FAIL;
static int ACCOUNT3_REG = REGISTER_FAIL;
static int ACCOUNTP2P_REG = REGISTER_FAIL;
static int press_down = FALSE;
static int CURRENT_CALLS = 0;
static int regcount = 0;
static char DTMF_STRING[32] = "\0";
static int SCHEDULE[31];
struct VLC_INFO vlcInfo[64];
struct CALL_LOG callInfo;
static redis_client_t *redisClient;

redis_client_t* create_redis_pool_client(const char *socket_path) {
    redis_client_t *client = (redis_client_t *) malloc(sizeof(redis_client_t));
    client->socket_path = socket_path;
    pthread_mutex_init(&client->lock, NULL);
    for(int i = 0; i < 5; i++)
    {
        redisContext *c = redisConnectUnix(socket_path);
        if (c == NULL || c->err) {
            if (c) {
                printf("Connection error: %s\n", c->errstr);
                redisFree(c);
            } else {
                printf("Connection error: can't allocate redis context\n");
            }
            // 可以选择重试或退出
            continue;
        }
        client->context[i] = c;
        client->in_use[i] = FALSE;
        client->last_activity[i] = time(NULL);
    }
    
    return client;
}

const int redis_pool_get_context(redis_client_t *client) {
    pthread_mutex_lock(&client->lock);
    redisContext *context = NULL;
    int idx = -1;
    for(int i = 0;i < 5; i++)
    {
        if(!client->in_use[i])
        {
            idx = i;
            client->in_use[idx] = TRUE;
            context = client->context[idx];
            redisReply *reply = redisCommand(context, "PING");
            if (reply == NULL || context->err) {
                // 连接失效，尝试重连
                if(DEBUG)
                        printf( "redis connect failed, retry!\n" );
                redisFree(context);
                context = redisConnectUnix(client->socket_path);
                client->context[idx] = context;
            }
            freeReplyObject(reply);
            client->last_activity[idx] = time(NULL);
            break;
        }
    }
    pthread_mutex_unlock(&client->lock);
    if (context == NULL) {
        printf("No available connections in pool\n");
    }
    
    return idx;
}

// 释放连接
void redis_pool_release_connection(redis_client_t *client, const int id) {
    pthread_mutex_lock(&client->lock);

    client->in_use[id] = FALSE;
    
    pthread_mutex_unlock(&client->lock);
}

void redis_command_safe(const char *format, const char *value) {
    const int id = redis_pool_get_context(redisClient);
    if (id != -1) {
        redisReply *reply = (redisReply *) redisCommand(redisClient->context[id], format, value);
        if (reply) {
            // 处理回复
            freeReplyObject(reply);
        }
        redis_pool_release_connection(redisClient, id);
    }
}

int ensure_connected(redisContext *context) {
    if (context == NULL || context->err) {
        if (context) {
            redisFree(context);
        }
        context = redisConnectUnix(unix_socket_path);
        if (context == NULL || context->err) {
            return -1; // 连接失败
        }
    }
    return 0;
}

void reset_callInfo()
{
	memset(&callInfo,0,sizeof(callInfo));
	callInfo.duration = 0;
}

void *write_opnlog()
{
	char * pKey = "writelog-channel";
	redisReply *pReply, *pSubReply;
	redisContext *redisLogContext = NULL;
    redisLogContext = redisConnectUnix(unix_socket_path);

restart:
    FILE *logFp = fopen(SIPLOGPATH, "a");
    log_add_fp(logFp, LOG_INFO);
    
	while(1)
	{
        pReply = NULL;
        if (ensure_connected(redisLogContext) == 0) {
            pReply = redisCommand( redisLogContext, "BRPOP %s 60", pKey );
            if(pReply != NULL && pReply->type == REDIS_REPLY_ARRAY){
                pSubReply = NULL;
                if ( pReply->elements == 2 )
                {
                    pSubReply = pReply->element[1];
                    
                    if(DEBUG)
                        printf( "opnLog [%s]\n", pSubReply->str );
                    if(strcmp(pSubReply->str, "log_clean") == 0)
                    {
                        fclose(logFp);
                        freeReplyObject( pReply );
                        sleep(5);
                        goto restart;
                    }
                    else
                        log_info("%s", pSubReply->str);
                }
            }
        }
		if(pReply != NULL) freeReplyObject( pReply );
		usleep(200*1000);
	}

    redisFree( redisLogContext );
}

void writeLog(char *type,char *action, char *remark)
{
	char logstr[256];
	sprintf(logstr, "%s,%s,%s", type, action, remark);
    redis_command_safe("LPUSH writelog-channel %s", logstr);
}

//发送状态指令
void state_event(char *info)
{
    redis_command_safe("PUBLISH volume-event-channel %s", info);
}

//发送屏幕显示指令
void screen_control(char *cmd)
{
    redis_command_safe("PUBLISH screen-channel %s", cmd);
}

void insert_call_log()
{
	FILE *cdr_fp = fopen(CDR_CSV, "a");
	//日期,开始时间,结束时间,账号,号码,通话时长,方向,状态,描述
	fprintf(cdr_fp, "%s,%s,%s,%s,%s,%d,%s,%s,%s\n",
		callInfo.date, callInfo.start, callInfo.end, callInfo.acc, callInfo.num, callInfo.duration,
			callInfo.direct, callInfo.status, callInfo.desc);
	fclose(cdr_fp);
	reset_callInfo();
}

void setIncomingCallInfo(char *accuri, char *num)
{
	char acc[32];
	time_t nSeconds;
	struct tm *pTM;
	int year, month, hour;

	time(&nSeconds);
	pTM = localtime(&nSeconds);

	year = pTM->tm_year + 1900;
	month = pTM->tm_mon + 1;

	memset(acc,0,sizeof(acc));

	strcpy(callInfo.direct,"in");
	sprintf(callInfo.date, "%d-%02d-%02d", year, month, pTM->tm_mday);
	sprintf(callInfo.start, "%02d:%02d:%02d", pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
	//strncpy(acc, accuri, strchr(accuri,'@') - accuri);
	//strcpy(callInfo.acc, acc + 4);
	strcpy(callInfo.acc, accuri);
	strcpy(callInfo.num, num);
	strcpy(callInfo.status, "MISSED CALL");
}

void setOutgoingCallInfo(char *accuri, char *num)
{
	char acc[32];
	time_t nSeconds;
	struct tm *pTM;
	int year, month, hour;

	time(&nSeconds);
	pTM = localtime(&nSeconds);

	year = pTM->tm_year + 1900;
	month = pTM->tm_mon + 1;

	memset(acc,0,sizeof(acc));

	strcpy(callInfo.direct,"out");
	sprintf(callInfo.date, "%d-%02d-%02d", year, month, pTM->tm_mday);
	sprintf(callInfo.start, "%02d:%02d:%02d", pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
	//strncpy(acc, accuri, strchr(accuri,'@') - accuri);
	//strcpy(callInfo.acc, acc + 4);
	strcpy(callInfo.acc, accuri);
	strcpy(callInfo.num, num);
	strcpy(callInfo.status, "NO ANSWER");
}

void setAnswerCallInfo()
{
	time_t nSeconds;

	time(&nSeconds);
	callInfo.answerTime = nSeconds;
	strcpy(callInfo.status, "ANSWERED");
}

void setClosedCallInfo(char *param)
{
	time_t nSeconds;
	struct tm *pTM;

	time(&nSeconds);
	pTM = localtime(&nSeconds);
	sprintf(callInfo.end, "%02d:%02d:%02d", pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
	strcpy(callInfo.desc, param);
	if(callInfo.answerTime)
	{
		callInfo.duration = nSeconds - callInfo.answerTime;
	}
	insert_call_log();
}

/*
*   @file 文件路径
*   @value 命令结果指针
*   @lenth 存储结果长度
*/
int GetCmdValue(char *file, char *key, char *value, int lenth)
{
	dictionary  *   ini ;
	ini = iniparser_load(file);
	
    if (ini==NULL) {
        fprintf(stderr, "cannot parse file: %s\n", file);
        return FALSE ;
    }
	strcpy(value, iniparser_getstring(ini, key, ""));
	if(DEBUG)
		printf("keystr: \"%s\" ; value: %s.\n", key, value);
	iniparser_freedict(ini);
	return TRUE;
}

char *strrpl(char *s, const char *s1, const char *s2)
{
    char *ptr;
    while (ptr = strstr(s, s1)) /* 如果在s中找到s1 */
    {
        memmove(ptr + strlen(s2) , ptr + strlen(s1), strlen(ptr) - strlen(s1) + 1);
        memcpy(ptr, &s2[0], strlen(s2));
    }
    return s;
}

//控制功放的开启/关闭,TRUE/FALSE
void amplifier_switch(Boolean enable){
    if(enable)
        system("echo 1 > /sys/class/gpio/gpio5/value");
    else
        system("echo 0 > /sys/class/gpio/gpio5/value");
}

Boolean get_ip(char * pv)
{
        FILE * fp;
        char cmdbuf[128], value[32];
        char *tmp=NULL;
        sprintf(cmdbuf,"/sbin/ifconfig eth1 | grep 'inet ' | awk '{print $2}'");
        fp = popen(cmdbuf, "r");
        if (fp == NULL) {
                // printf("Fail to open pipe\n");
                return FALSE;
        }

        memset(value,0,sizeof(value));

        if (!feof(fp) && fgets(value,32,fp) != NULL)
            ;
            // printf("value:%s\n", value);
        else {
            printf("ip wrong\n");
            return FALSE;
        }

        pclose(fp);

        if(strlen(value) < 10)
            return FALSE;
        if(tmp = strstr(value,"\n"))
        {
            *tmp = '\0';
        }
        strcpy(pv,value);
        return TRUE;
}

Boolean get_mac(char * pv)
{
        FILE * fp;
        char cmdbuf[128], value[32];
        char *tmp=NULL;
        sprintf(cmdbuf,"ifconfig eth1 | grep ether | tr -d : | awk '{print $2}'");
        fp = popen(cmdbuf, "r");
        if (fp == NULL) {
                return FALSE;
        }

        memset(value,0,sizeof(value));

        if (!feof(fp) && fgets(value,32,fp) != NULL)
            ;
            // printf("value:%s\n", value);
        else {
            printf("ip wrong\n");
            return FALSE;
        }

        pclose(fp);

        if(strlen(value) < 10)
            return FALSE;
        if(tmp = strstr(value,"\n"))
        {
            *tmp = '\0';
        }
        strcpy(pv,value);
        return TRUE;
}

int PlayIP()
{
    //add by ssc 2019-12-26
    char language[32];
    GetCmdValue(SPK_CONF, "system:language", language, sizeof(language));

    char ipaddr[16],cmd_line[240];
    system("/usr/bin/amixer -q sset 'Master',0 29");
    amplifier_switch(TRUE); //开启功放
    if(!get_ip(ipaddr)) {
        if (strcmp(language, "en") == 0){
            system("mpg123 -r 48000 /usr/share/sounds/en/get_ip_failed.mp3");
        }else{
            system("mpg123 -r 48000 /usr/share/sounds/cn/get_ip_failed.mp3");
        }
        return 0;
    }

    if (strcmp(language, "en") == 0){
        strcpy(cmd_line,"cd /usr/share/sounds/en/;mpg123 -r 48000 ");
    }else{
        strcpy(cmd_line,"cd /usr/share/sounds/cn/;mpg123 -r 48000 ");
    }

    int i;
    for (i=0;i<strlen(ipaddr);i++){
        switch(ipaddr[i])
        {
            case '0':
                strcat(cmd_line,"digit-0.mp3 ");
                break;
            case '1':
                strcat(cmd_line,"digit-1.mp3 ");
                break;
            case '2':
                strcat(cmd_line,"digit-2.mp3 ");
                break;
            case '3':
                strcat(cmd_line,"digit-3.mp3 ");
                break;
            case '4':
                strcat(cmd_line,"digit-4.mp3 ");
                break;
            case '5':
                strcat(cmd_line,"digit-5.mp3 ");
                break;
            case '6':
                strcat(cmd_line,"digit-6.mp3 ");
                break;
            case '7':
                strcat(cmd_line,"digit-7.mp3 ");
                break;
            case '8':
                strcat(cmd_line,"digit-8.mp3 ");
                break;
            case '9':
                strcat(cmd_line,"digit-9.mp3 ");
                break;
            case '.':
                strcat(cmd_line,"dian.mp3 ");
                break;
        }
    }
    // printf("cmd-line:%s\n", cmd_line);
    system(cmd_line);

    amplifier_switch(FALSE); //关闭功放
    system("/etc/scripts/set_volume.sh resume");
    writeLog( FUN, "Play IP Address", ipaddr );
    return 0;
}

void action_url(char *url)
{
    char cmd[320];
    sprintf(cmd,"curl -k --connect-timeout 5 '%s' 2>/dev/null &",url);
    if(DEBUG)
        printf( "Command: %s\n", cmd);
    system(cmd);
}

void playBeep()
{
    char beep[8];
    GetCmdValue(VOL_CONF, "volume:key_beep", beep, sizeof(beep));
    if(strcmp(beep, "yes") == 0)
    {
        if(BARESIP_STATUS == IDLE || BARESIP_STATUS == OFFLINE)
            system("/usr/sbin/redis-cli PUBLISH volume-event-channel '{\"name\": \"SIP\", \"action\": \"on\"}'");
        system("mpg123 /home/new-speaker/target/share/sounds/beep.mp3");
        usleep(400*1000);
        if(BARESIP_STATUS == IDLE || BARESIP_STATUS == OFFLINE)
            system("/usr/sbin/redis-cli PUBLISH volume-event-channel '{\"name\": \"SIP\", \"action\": \"off\"}'"); //关闭功放
    }
}

//发送控制指令
void control_cmd(int channel, char *cmd)
{
    char redisCmd[256];
    switch (channel)
    {
        case BARESIP_CHAN:
            if(DEBUG)
                printf( "control-channel [%s]\n", cmd );
            redis_command_safe("LPUSH control-channel %s", cmd );
            break;
        case GPIO_CHAN:
            if(DEBUG)
                printf( "output-channel [%s]\n", cmd );
            redis_command_safe("LPUSH output-channel %s", cmd );
            break;
        default:
            redis_command_safe("LPUSH control-channel %s", cmd );
            break;
    }
}

void relayControl(char *id, int enable){
    char cmd[64];
    int channel = GPIO_CHAN;
    if(enable){
        char output_type[32];
        GetCmdValue(SPK_CONF, "relay_ctrl:type", output_type, sizeof(output_type));
        sprintf(cmd,"{\"id\":\"%s\",\"type\":\"%s\"}",id,output_type);
        control_cmd(channel,cmd);
    }
    else
    {
        sprintf(cmd,"{\"id\":\"%s\",\"type\":\"Off\"}",id);
        control_cmd(channel,cmd);
    }
}

void screenControl(int enable){
    char cmd[64], output_type[16];
    GetCmdValue(SPK_CONF, "screen_ctrl:type", output_type, sizeof(output_type));
    if(enable){
        sprintf(cmd,"{\"action\": \"on\", \"id\": %s}",output_type);
        screen_control(cmd);
    }
    else
    {
        sprintf(cmd,"{\"action\": \"off\", \"id\": %s}",output_type);
        screen_control(cmd);
    }
}

//relay延时控制
void *relayDelayControl()
{
    char delay[8];
    GetCmdValue(SPK_CONF, "relay_ctrl:duration", delay, sizeof(delay));
    usleep(atoi(delay)*1000*1000);
    relayControl(GPIO_RELAY, GPIO_OFF);
    if(DEBUG)
        printf( "GPIO_RELAY set off\n" );
}

//srceen延时控制
void *screenDelayControl()
{
    char delay[8];
    GetCmdValue(SPK_CONF, "screen_ctrl:duration", delay, sizeof(delay));
    usleep(atoi(delay)*1000*1000);
    screenControl(FALSE);
    if(DEBUG)
        printf( "screen set off\n" );
}

void *key1ScreenDelayControl()
{
    char delay[8], srceen_id[16], strtmp[64];
    GetCmdValue(SPK_CONF, "intercom:screen_duration", delay, sizeof(delay));
    usleep(atoi(delay)*1000*1000);
    GetCmdValue(SPK_CONF, "intercom:trigger_id", srceen_id, sizeof(srceen_id));
    sprintf(strtmp,"{\"action\": \"off\", \"id\": %s}",srceen_id);
    screen_control(strtmp);
    if(DEBUG)
        printf( "screen set off\n" );
}

void *key2ScreenDelayControl()
{
    char delay[8], srceen_id[16], strtmp[64];
    GetCmdValue(SPK_CONF, "intercom:screen_2_duration", delay, sizeof(delay));
    usleep(atoi(delay)*1000*1000);
    GetCmdValue(SPK_CONF, "intercom:trigger_2_id", srceen_id, sizeof(srceen_id));
    sprintf(strtmp,"{\"action\": \"off\", \"id\": %s}",srceen_id);
    screen_control(strtmp);
    if(DEBUG)
        printf( "screen set off\n" );
}

//呼入触发
void incomingTrigger()
{
    char relay_call_state[8],relay_state_info[16],relay_mode[16];

    GetCmdValue(SPK_CONF, "relay_ctrl:call_state", relay_call_state, sizeof(relay_call_state));
    GetCmdValue(SPK_CONF, "relay_ctrl:state_info", relay_state_info, sizeof(relay_state_info));
    if( strcmp(relay_call_state, "yes") == 0 && ( strcmp(relay_state_info, "incoming") == 0 || strcmp(relay_state_info, "both") == 0 ) )
    {
        relayControl(GPIO_RELAY, GPIO_ON);
        if(DEBUG)
            printf( "GPIO_RELAY set on\n" );
        //gpio触发后判断是否为延时复位
        GetCmdValue(SPK_CONF, "relay_ctrl:mode", relay_mode, sizeof(relay_mode));
        if( strcmp(relay_mode, "delay") == 0 )
        {
            int ret_thrd_delay;
            pthread_t thread_delay;
            ret_thrd_delay = pthread_create(&thread_delay, NULL, relayDelayControl, NULL);
            if (ret_thrd_delay != 0) {
                printf("create thread_delay relayDelayControl failed\n");
                return;
            }
            pthread_detach(thread_delay);
        }
    }
}

//呼出触发
void outgoingTrigger()
{
    char relay_call_state[8],relay_state_info[16],relay_mode[16];
    char screen_call_state[8], screen_state_info[16], screen_mode[16];

    GetCmdValue(SPK_CONF, "relay_ctrl:call_state", relay_call_state, sizeof(relay_call_state));
    GetCmdValue(SPK_CONF, "relay_ctrl:state_info", relay_state_info, sizeof(relay_state_info));
    if( strcmp(relay_call_state, "yes") == 0 && ( strcmp(relay_state_info, "outgoing") == 0 || strcmp(relay_state_info, "both") == 0 ) )
    {
        relayControl(GPIO_RELAY, GPIO_ON);
        if(DEBUG)
            printf( "GPIO_RELAY set on\n" );
        //gpio触发后判断是否为延时复位
        GetCmdValue(SPK_CONF, "relay_ctrl:mode", relay_mode, sizeof(relay_mode));
        if( strcmp(relay_mode, "delay") == 0 )
        {
            int ret_thrd_delay;
            pthread_t thread_delay;
            ret_thrd_delay = pthread_create(&thread_delay, NULL, relayDelayControl, NULL);
            if (ret_thrd_delay != 0) {
                printf("create thread_delay relayDelayControl failed\n");
                return;
            }
            pthread_detach(thread_delay);
        }
    }
}

//应答触发
void answeredTrigger()
{
    char cmd[MAX_PIPE_BUFSIZE];
    char local_beep[4], local_beep_file[64], local_beep_volume[4];
    char remote_beep[4], remote_beep_file[64], remote_beep_volume[4];
    char relay_call_state[8],relay_state_info[16],relay_mode[16];
    char screen_call_state[8], screen_state_info[16], screen_mode[16];

	setAnswerCallInfo();
    //判断RELAY是否需要应答复位
    GetCmdValue(SPK_CONF, "relay_ctrl:mode", relay_mode, sizeof(relay_mode));
    if( strcmp(relay_mode, "answered") == 0 )
    {
        relayControl(GPIO_RELAY, GPIO_OFF);
        if(DEBUG)
            printf( "GPIO_RELAY set off\n" );
    }
    else
    {
        GetCmdValue(SPK_CONF, "relay_ctrl:call_state", relay_call_state, sizeof(relay_call_state));
        GetCmdValue(SPK_CONF, "relay_ctrl:state_info", relay_state_info, sizeof(relay_state_info));
        if( strcmp(relay_call_state, "yes") == 0 && strcmp(relay_state_info, "answered") == 0 )
        {
            relayControl(GPIO_RELAY, GPIO_ON);
            if(DEBUG)
                printf( "GPIO_RELAY set on\n" );
            //gpio触发后判断是否为延时复位
            if( strcmp(relay_mode, "delay") == 0 )
            {
                int ret_thrd_delay;
                pthread_t thread_delay;
                ret_thrd_delay = pthread_create(&thread_delay, NULL, relayDelayControl, NULL);
                if (ret_thrd_delay != 0) {
                    printf("create thread_delay relayDelayControl failed\n");
                    return;
                }
                pthread_detach(thread_delay);
            }
        }
    }
    //触发应答提示音播放
    GetCmdValue(SPK_CONF, "sip_function:answer_local_beep", local_beep, sizeof(local_beep));
    if(strcmp(local_beep, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "sip_function:answer_local_beep_file", local_beep_file, sizeof(local_beep_file));
        GetCmdValue(SPK_CONF, "sip_function:answer_local_beep_volume", local_beep_volume, sizeof(local_beep_volume));
        sprintf(cmd,"{\"cmd\":\"mixausrc_dec_start\",\"data\":\"aufile /usr/share/baresip/%s 10 %s\"}", local_beep_file, local_beep_volume);
        control_cmd(BARESIP_CHAN, cmd);
    }
    GetCmdValue(SPK_CONF, "sip_function:answer_remote_beep", remote_beep, sizeof(remote_beep));
    if(strcmp(remote_beep, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "sip_function:answer_remote_beep_file", remote_beep_file, sizeof(remote_beep_file));
        GetCmdValue(SPK_CONF, "sip_function:answer_remote_beep_volume", remote_beep_volume, sizeof(remote_beep_volume));
        sprintf(cmd,"{\"cmd\":\"mixausrc_enc_start\",\"data\":\"aufile /usr/share/baresip/%s 10 %s\"}", remote_beep_file, remote_beep_volume);
        control_cmd(BARESIP_CHAN, cmd);
    }
}

//挂断触发
void hangupTrigger(char *param)
{
    char cmd[MAX_PIPE_BUFSIZE];
    char relay_call_state[8],relay_state_info[16],relay_mode[16];
    char call_state[8],state_info[16],output_mode[16], beep[4], beep_file[64], beep_volume[4];
    char screen_call_state[8], screen_state_info[16], screen_mode[16];

    //触发挂断提示音播放
    GetCmdValue(SPK_CONF, "sip_function:hangup_beep", beep, sizeof(beep));
    if(strcmp(beep, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "sip_function:hangup_beep_file", beep_file, sizeof(beep_file));
        GetCmdValue(SPK_CONF, "sip_function:hangup_beep_volume", beep_volume, sizeof(beep_volume));
        sprintf(cmd,"{\"cmd\":\"setaudiotempvol\",\"data\":\"%s\"}",beep_volume);
        control_cmd(BARESIP_CHAN, cmd);
        sprintf(cmd,"{\"cmd\":\"play\",\"data\":\"%s:0\"}", beep_file);
        control_cmd(BARESIP_CHAN, cmd);
    }
    setClosedCallInfo(param);
    //判断RELAY是否需要挂断复位
    GetCmdValue(SPK_CONF, "relay_ctrl:mode", relay_mode, sizeof(relay_mode));
    if( strcmp(relay_mode, "hangup") == 0 )
    {
        relayControl(GPIO_RELAY, GPIO_OFF);
        if(DEBUG)
            printf( "GPIO_RELAY set off\n" );
    }
    else
    {
        //否则判断是否需要执行挂断触发
        GetCmdValue(SPK_CONF, "relay_ctrl:call_state", relay_call_state, sizeof(relay_call_state));
        GetCmdValue(SPK_CONF, "relay_ctrl:state_info", relay_state_info, sizeof(relay_state_info));
        if( strcmp(relay_call_state, "yes") == 0){
            if(strcmp(relay_state_info, "hangup") == 0 )
            {
                relayControl(GPIO_RELAY, GPIO_ON);
                if(DEBUG)
                    printf( "GPIO_RELAY set on\n" );
                //gpio触发后延时复位
                int ret_thrd_delay;
                pthread_t thread_delay;
                ret_thrd_delay = pthread_create(&thread_delay, NULL, relayDelayControl, NULL);
                if (ret_thrd_delay != 0) {
                    printf("create thread_delay relayDelayControl failed\n");
                    return;
                }
                pthread_detach(thread_delay);
            }
            else
            {
                relayControl(GPIO_RELAY, GPIO_OFF);
                if(DEBUG)
                    printf( "GPIO_RELAY set off\n" );
            }
        }
    }
}

//DTMF检测
void dtmfDetect(char *dtmf)
{
    char dtmf_event[8],dtmf_code[32],relay_mode[16];
    char srceen_dtmf_event[8],srceen_dtmf_code[32],srceen_mode[16];
    char dtmf_tmp[32] = "\0";
    int dtmflen,codelen,srceen_codelen, reset = FALSE;
    GetCmdValue(SPK_CONF, "relay_ctrl:dtmf", dtmf_event, sizeof(dtmf_event));
    GetCmdValue(SPK_CONF, "screen_ctrl:dtmf", srceen_dtmf_event, sizeof(srceen_dtmf_event));
    if( strcmp(dtmf_event, "yes") == 0 ||  strcmp(srceen_dtmf_event, "yes") == 0 )
    {
        strcat(DTMF_STRING,dtmf);
        if(DEBUG)
            printf( "DTMF is [%s]\n", DTMF_STRING );
        dtmflen = strlen(DTMF_STRING);
        if(strcmp(dtmf_event, "yes") == 0 )
        {
            GetCmdValue(SPK_CONF, "relay_ctrl:dtmf_code", dtmf_code, sizeof(dtmf_code));
            codelen = strlen(dtmf_code);
            if( dtmflen >= codelen )
            {
                strcpy(dtmf_tmp, DTMF_STRING + dtmflen - codelen);
                if( strcmp(dtmf_tmp, dtmf_code) == 0 ){
                    relayControl(GPIO_RELAY, GPIO_ON);
                    if(DEBUG)
                        printf( "GPIO_RELAY set on\n" );
                    //gpio触发后判断是否为延时复位
                    GetCmdValue(SPK_CONF, "relay_ctrl:mode", relay_mode, sizeof(relay_mode));
                    if( strcmp(relay_mode, "delay") == 0 )
                    {
                        int ret_thrd_delay;
                        pthread_t thread_delay;
                        ret_thrd_delay = pthread_create(&thread_delay, NULL, relayDelayControl, NULL);
                        if (ret_thrd_delay != 0) {
                            printf("create thread_delay relayDelayControl failed\n");
                            return;
                        }
                        pthread_detach(thread_delay);
                    }
                    reset = TRUE;
                }
            }
        }
        if(strcmp(srceen_dtmf_event, "yes") == 0 )
        {
            GetCmdValue(SPK_CONF, "screen_ctrl:dtmf_code", srceen_dtmf_code, sizeof(srceen_dtmf_code));
            srceen_codelen = strlen(srceen_dtmf_code);
            if( dtmflen >= srceen_codelen )
            {
                strcpy(dtmf_tmp, DTMF_STRING + dtmflen - srceen_codelen);
                if( strcmp(dtmf_tmp, srceen_dtmf_code) == 0 ){
                    screenControl(TRUE);
                    if(DEBUG)
                        printf( "screen set on\n" );
                    //触发后判断是否为延时复位
                    GetCmdValue(SPK_CONF, "screen_ctrl:mode", srceen_mode, sizeof(srceen_mode));
                    if( strcmp(srceen_mode, "delay") == 0 )
                    {
                        int ret_thrd_delay;
                        pthread_t thread_delay;
                        ret_thrd_delay = pthread_create(&thread_delay, NULL, screenDelayControl, NULL);
                        if (ret_thrd_delay != 0) {
                            printf("create thread_delay screenDelayControl failed\n");
                            return;
                        }
                        pthread_detach(thread_delay);
                    }
                    reset = TRUE;
                }
            }
        }
        if(reset) memset(DTMF_STRING, 0, sizeof(DTMF_STRING));
    }
}

//baresip状态监控
void ProcessReply( redisReply * pReply )
{
    cJSON *pJson;
    redisReply * pSubReply = NULL;
    char callerid[128],strtmp[256];

    if ( pReply != NULL && pReply->elements == 3 )
    {
        pSubReply = pReply->element[2];
        if(DEBUG)
            printf( "Msg [%s]\n", pSubReply->str );
        pJson =  cJSON_Parse(pSubReply->str);
        if ( pJson ) {
            if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "incoming") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_ESTABLISHED") == 0)
            {
                //通话
                writeLog( STA, "CALL ESTABLISHED", "incoming" );
                //amplifier_switch(TRUE);
                BARESIP_STATUS = INUSE;
                answeredTrigger();
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "outgoing") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_ESTABLISHED") == 0)
            {
                //通话
                writeLog( STA, "CALL ESTABLISHED", "outgoing" );
                //amplifier_switch(TRUE);
                BARESIP_STATUS = INUSE;
                answeredTrigger();
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "incoming") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_CLOSED") == 0)
            {
                //挂断
                hangupTrigger(cJSON_GetObjectItem(pJson, "param")->valuestring);
                writeLog( STA, "CALL CLOSED", cJSON_GetObjectItem(pJson, "param")->valuestring );
                if(BARESIP_STATUS != OFFLINE){
                    BARESIP_STATUS = IDLE;
                }
                if(strlen(DTMF_STRING))
                    memset(DTMF_STRING, 0, sizeof(DTMF_STRING));
                
                CURRENT_CALLS --;
                if(CURRENT_CALLS < 1)
                {
                    //amplifier_switch(FALSE);
                    CURRENT_CALLS = 0;
                }
                else
                {
                    control_cmd(BARESIP_CHAN, "{\"cmd\":\"resume\",\"data\":\"\"}");
                }
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "outgoing") == 0 && 
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_CLOSED") == 0)
            {
                //挂断
                hangupTrigger(cJSON_GetObjectItem(pJson, "param")->valuestring);
                writeLog( STA, "CALL CLOSED", cJSON_GetObjectItem(pJson, "param")->valuestring );
                if(BARESIP_STATUS != OFFLINE){
                    BARESIP_STATUS = IDLE;
                }
                CURRENT_KEY = UNKEY;
                if(strlen(DTMF_STRING))
                    memset(DTMF_STRING, 0, sizeof(DTMF_STRING));
                
                CURRENT_CALLS --;
                if(CURRENT_CALLS < 1)
                {
                    CURRENT_CALLS = 0;
                }
                else
                {
                    control_cmd(BARESIP_CHAN, "{\"cmd\":\"resume\",\"data\":\"\"}");
                }
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "incoming") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_INCOMING") == 0)
            {
                //呼入振铃状态
                writeLog( STA, "INCOMING CALL", cJSON_GetObjectItem(pJson, "peeruri")->valuestring );
                CURRENT_CALLS ++;
                //amplifier_switch(TRUE);
                if(BARESIP_STATUS == IDLE)
                {
                    BARESIP_STATUS = RING;
                    CURRENT_KEY = UNKEY;
                    incomingTrigger();
                }
				memset(callerid, 0, sizeof(callerid));
				if(strchr(cJSON_GetObjectItem(pJson, "peeruri")->valuestring,'@'))
					strncpy(callerid, cJSON_GetObjectItem(pJson, "peeruri")->valuestring,
						strchr(cJSON_GetObjectItem(pJson, "peeruri")->valuestring,'@') - cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
				else
					strcpy(callerid, cJSON_GetObjectItem(pJson, "peeruri")->valuestring);

				setIncomingCallInfo(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, callerid + 4);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "outgoing") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_OUTGOING") == 0)
            {
                //呼出回铃状态
                writeLog( STA, "OUTGOING CALL", cJSON_GetObjectItem(pJson, "peeruri")->valuestring );
                CURRENT_CALLS ++;
                //amplifier_switch(TRUE);
                if(BARESIP_STATUS == IDLE || BARESIP_STATUS == DIALING)
                {
                    BARESIP_STATUS = RINGING;
                    outgoingTrigger();
                }
				memset(callerid, 0, sizeof(callerid));
				if(strchr(cJSON_GetObjectItem(pJson, "peeruri")->valuestring,'@'))
					strncpy(callerid, cJSON_GetObjectItem(pJson, "peeruri")->valuestring,
						strchr(cJSON_GetObjectItem(pJson, "peeruri")->valuestring,'@') - cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
				else
					strncpy(callerid, cJSON_GetObjectItem(pJson, "peeruri")->valuestring,
						strchr(cJSON_GetObjectItem(pJson, "peeruri")->valuestring,';') - cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
                
				setOutgoingCallInfo(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, callerid + 4);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_DTMF_START") == 0)
            {
                //接收DTMF
                writeLog( FUN, "RECEIVE DTMF", cJSON_GetObjectItem(pJson, "param")->valuestring );
                dtmfDetect(cJSON_GetObjectItem(pJson, "param")->valuestring);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "REGISTER_OK") == 0)
            {
                //如果是注册成功点亮指示灯
                char enac1[8],enac2[8],enac3[8],accountaor1[192], accountaor2[192], accountaor3[192];
                char account1[32], domain1[128], account2[32], domain2[128], account3[32], domain3[128];
                if(BARESIP_STATUS == OFFLINE || BARESIP_STATUS == DIALING || strcmp(cJSON_GetObjectItem(pJson, "class")->valuestring, "init register") == 0)
                {
                    BARESIP_STATUS = IDLE;
                    CURRENT_CALLS = 0;
                }
				if(cJSON_GetObjectItem(pJson, "accountaor") != NULL)
				{
					GetCmdValue(SPK_CONF, "account_info_1:enable", enac1, sizeof(enac1));
					if(strcmp(enac1, "yes") == 0 && ACCOUNT1_REG == REGISTER_FAIL)
					{
						GetCmdValue(SPK_CONF, "account_info_1:username", account1, sizeof(account1));
						GetCmdValue(SPK_CONF, "account_info_1:domain", domain1, sizeof(domain1));
                        if(strlen(domain1) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_1:server", domain1, sizeof(domain1));
                        }
						memset(accountaor1, '\0', sizeof(accountaor1));
						sprintf(accountaor1, "sip:%s@%s", account1, domain1);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor1) == 0)
						{
							ACCOUNT1_REG = REGISTER_OK;
							cJSON_Delete(pJson);
                            sprintf(strtmp,"Primary SIP Account <%s>",accountaor1);
                            writeLog( STA, "SIP REGISTERED", strtmp );
							return;
						}
					}
					GetCmdValue(SPK_CONF, "account_info_2:enable", enac2, sizeof(enac2));
					if(strcmp(enac2, "yes") == 0 && ACCOUNT2_REG == REGISTER_FAIL)
					{
						GetCmdValue(SPK_CONF, "account_info_2:username", account2, sizeof(account2));
						GetCmdValue(SPK_CONF, "account_info_2:domain", domain2, sizeof(domain2));
                        if(strlen(domain2) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_2:server", domain2, sizeof(domain2));
                        }
						memset(accountaor2, '\0', sizeof(accountaor2));
						sprintf(accountaor2, "sip:%s@%s", account2, domain2);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor2) == 0)
						{
							ACCOUNT2_REG = REGISTER_OK;
							cJSON_Delete(pJson);
                            sprintf(strtmp,"Secondary SIP Account-1 <%s>",accountaor2);
                            writeLog( STA, "SIP REGISTERED", strtmp );
							return;
						}
					}
					GetCmdValue(SPK_CONF, "account_info_3:enable", enac3, sizeof(enac3));
					if(strcmp(enac3, "yes") == 0 && ACCOUNT3_REG == REGISTER_FAIL)
					{
						GetCmdValue(SPK_CONF, "account_info_3:username", account3, sizeof(account3));
						GetCmdValue(SPK_CONF, "account_info_3:domain", domain3, sizeof(domain3));
                        if(strlen(domain3) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_3:server", domain3, sizeof(domain3));
                        }
						memset(accountaor3, '\0', sizeof(accountaor3));
						sprintf(accountaor3, "sip:%s@%s", account3, domain3);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor3) == 0)
						{
							ACCOUNT3_REG = REGISTER_OK;
							cJSON_Delete(pJson);
                            sprintf(strtmp,"Secondary SIP Account-2 <%s>",accountaor3);
                            writeLog( STA, "SIP REGISTERED", strtmp );
							return;
						}
					}
				}
				regcount ++;
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "REGISTER_FAIL") == 0 )
            {
                char enac1[8],enac2[8],enac3[8],accountaor1[192], accountaor2[192], accountaor3[192];
                char account1[32], domain1[128], account2[32], domain2[128], account3[32], domain3[128];

				if(cJSON_GetObjectItem(pJson, "accountaor") != NULL)
				{
					GetCmdValue(SPK_CONF, "account_info_1:enable", enac1, sizeof(enac1));
					if(strcmp(enac1, "yes") == 0 && ACCOUNT1_REG == REGISTER_OK)
					{
						GetCmdValue(SPK_CONF, "account_info_1:username", account1, sizeof(account1));
						GetCmdValue(SPK_CONF, "account_info_1:domain", domain1, sizeof(domain1));
                        if(strlen(domain1) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_1:server", domain1, sizeof(domain1));
                        }
						memset(accountaor1, '\0', sizeof(accountaor1));
						sprintf(accountaor1, "sip:%s@%s", account1, domain1);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor1) == 0)
						{
							ACCOUNT1_REG = REGISTER_FAIL;
                            if( ACCOUNT2_REG == REGISTER_FAIL && ACCOUNT3_REG == REGISTER_FAIL && ACCOUNTP2P_REG == REGISTER_FAIL ){
                                BARESIP_STATUS = OFFLINE;
                            }
                            sprintf(strtmp,"Primary Account REGISTER FAILED <%s>",accountaor1);
                            writeLog( STA, strtmp, cJSON_GetObjectItem(pJson, "param")->valuestring );
                            cJSON_Delete(pJson);
							return;
						}
					}
					GetCmdValue(SPK_CONF, "account_info_2:enable", enac2, sizeof(enac2));
					if(strcmp(enac2, "yes") == 0 && ACCOUNT2_REG == REGISTER_OK)
					{
						GetCmdValue(SPK_CONF, "account_info_2:username", account2, sizeof(account2));
						GetCmdValue(SPK_CONF, "account_info_2:domain", domain2, sizeof(domain2));
                        if(strlen(domain2) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_2:server", domain2, sizeof(domain2));
                        }
						memset(accountaor2, '\0', sizeof(accountaor2));
						sprintf(accountaor2, "sip:%s@%s", account2, domain2);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor2) == 0)
						{
                            ACCOUNT2_REG = REGISTER_FAIL;
                            if( ACCOUNT1_REG == REGISTER_FAIL && ACCOUNT3_REG == REGISTER_FAIL && ACCOUNTP2P_REG == REGISTER_FAIL ){
                                BARESIP_STATUS = OFFLINE;
                            }
                            sprintf(strtmp,"Secondary Account-1 REGISTER FAILED <%s>",accountaor2);
                            writeLog( STA, strtmp, cJSON_GetObjectItem(pJson, "param")->valuestring );
                            cJSON_Delete(pJson);
							return;
						}
					}
					GetCmdValue(SPK_CONF, "account_info_3:enable", enac3, sizeof(enac3));
					if(strcmp(enac3, "yes") == 0 && ACCOUNT3_REG == REGISTER_OK)
					{
						GetCmdValue(SPK_CONF, "account_info_3:username", account3, sizeof(account3));
						GetCmdValue(SPK_CONF, "account_info_3:domain", domain3, sizeof(domain3));
                        if(strlen(domain3) == 0)
                        {
                            GetCmdValue(SPK_CONF, "account_info_3:server", domain3, sizeof(domain3));
                        }
						memset(accountaor3, '\0', sizeof(accountaor3));
						sprintf(accountaor3, "sip:%s@%s", account3, domain3);
						if (strcmp(cJSON_GetObjectItem(pJson, "accountaor")->valuestring, accountaor3) == 0)
						{
                            ACCOUNT3_REG = REGISTER_FAIL;
                            if( ACCOUNT1_REG == REGISTER_FAIL && ACCOUNT2_REG == REGISTER_FAIL && ACCOUNTP2P_REG == REGISTER_FAIL ){
                                BARESIP_STATUS = OFFLINE;
                            }
                            sprintf(strtmp,"Secondary Account-2 REGISTER FAILED <%s>",accountaor3);
                            writeLog( STA, strtmp, cJSON_GetObjectItem(pJson, "param")->valuestring );
                            cJSON_Delete(pJson);
							return;
						}
					}
				}
				regcount ++;
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "EXIT") == 0)
            {
                //如果是注册失败熄灭指示灯
                BARESIP_STATUS = OFFLINE;
                ACCOUNT1_REG = REGISTER_FAIL;
                ACCOUNT2_REG = REGISTER_FAIL;
                ACCOUNT3_REG = REGISTER_FAIL;
                writeLog( STA, "APP EXIT", "" );
                CURRENT_CALLS = 0;
            }
            else if(cJSON_GetObjectItem(pJson, "event")->type == 1 && cJSON_GetObjectItem(pJson, "event")->valueint == 0){
                if(strncmp(cJSON_GetObjectItem(pJson, "data")->valuestring,"\n--- User Agents",16) == 0)
                {
                    char p2penable[8];
                    GetCmdValue(SPK_CONF, "account_info_p2p:enable", p2penable, sizeof(p2penable));
                    if(strcmp(p2penable,"yes") == 0)
                    {
                        BARESIP_STATUS = IDLE;
                        if(ACCOUNTP2P_REG == REGISTER_FAIL)
                        {
                            ACCOUNTP2P_REG = REGISTER_OK;
                            writeLog( STA, "P2P ACCOUNT", "Enabled" );
                        }
                    }
                    else if(ACCOUNTP2P_REG == REGISTER_OK)
                    {
                        ACCOUNTP2P_REG = REGISTER_FAIL;
                        writeLog( STA, "P2P ACCOUNT", "Disabled" );
                    }
                }
            }
            if(DEBUG)
                printf( "Baresip Status is: %d\n", BARESIP_STATUS );
        }else{
            printf( "parse failed!\n");
        }
        if(pJson != NULL) cJSON_Delete(pJson);
    }
}

/*
* 订阅baresip实时状态.
*/
void *baresip_status()
{
    redisContext * pContext = redisConnectUnix(unix_socket_path);

    if ( NULL == pContext || pContext->err == 1 )
    {
        printf( "%s\n", pContext->errstr );
        exit( -1 );
    }

    char * pKey = "session-channel";

    redisReply * pReply = redisCommand( pContext, "SUBSCRIBE %s", pKey );
    freeReplyObject( pReply );
    while ( redisGetReply( pContext, (void **)&pReply ) == REDIS_OK )
    {
        ProcessReply( pReply );
        freeReplyObject( pReply );
    }

    redisFree( pContext );
}

int get_line(int * line_id)
{
	char enac[8], strtmp[32];
	int id = 0;

	memset(line_id,-1,sizeof(line_id));
	for(int i = 1; i < 4;i ++)
	{
		bzero(strtmp, sizeof(strtmp));
		sprintf(strtmp,"account_info_%d:enable",i);
		GetCmdValue(SPK_CONF, strtmp, enac, sizeof(enac));
		if(strcmp(enac,"yes") == 0)
		{
			line_id[i] = id;
			id ++;
		}
	}
	return TRUE;
}

//按键动作
void keyAction(int key)
{
    char cmd[MAX_PIPE_BUFSIZE];
    char accountaor[192],strtmp[64];
    char account[32], enable[8],url[256],ipaddr[16],macaddr[16];
    char onekey_num[32],onekey_line[24],repress_cancel[8];
    int channel = BARESIP_CHAN, line_id[4];

    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    switch(key){
        case KEY://处理按键动作
            if( CURRENT_KEY == UNKEY || CURRENT_KEY == KEY )
            {
                switch(BARESIP_STATUS)
                {
                    case IDLE:
                        CURRENT_KEY = KEY;
                        BARESIP_STATUS = DIALING;
                        GetCmdValue(SPK_CONF, "intercom:onekey_num", onekey_num, sizeof(onekey_num));
                        GetCmdValue(SPK_CONF, "intercom:onekey_line", onekey_line, sizeof(onekey_line));
                        if(strlen(onekey_num) == 0) 
                        {
                            BARESIP_STATUS = IDLE;
                            break;
                        }
                        get_line(line_id);
                        if(strcmp(onekey_line,"auto") == 0)
                        {
                            if(DEBUG)
                                printf("ACCOUNT1 is %d, ACCOUNT2 is %d, ACCOUNT3 is %d\n",ACCOUNT1_REG,ACCOUNT2_REG,ACCOUNT3_REG);
                            if(ACCOUNT1_REG == REGISTER_OK)
                            {
                                if(line_id[1] == -1)
                                {
                                    BARESIP_STATUS = IDLE;
                                    break;
                                }
                                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[1]);
                            }
                            else if(ACCOUNT2_REG == REGISTER_OK)
                            {
                                if(line_id[2] == -1)
                                {
                                    BARESIP_STATUS = IDLE;
                                    break;
                                }
                                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[2]);
                            }
                            else if(ACCOUNT3_REG == REGISTER_OK)
                            {
                                if(line_id[3] == -1)
                                {
                                    BARESIP_STATUS = IDLE;
                                    break;
                                }
                                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[3]);
                            }
                            else
                            {
                                BARESIP_STATUS = IDLE;
                                break;
                            }
                        }
                        else if (strcmp(onekey_line, "account_info_1") == 0)
                        {
                            if(line_id[1] == -1 || ACCOUNT1_REG != REGISTER_OK)
                            {
                                BARESIP_STATUS = IDLE;
                                break;
                            }
                            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[1]);
                        }
                        else if (strcmp(onekey_line, "account_info_2") == 0)
                        {
                            if(line_id[2] == -1 || ACCOUNT2_REG != REGISTER_OK)
                            {
                                BARESIP_STATUS = IDLE;
                                break;
                            }
                            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[2]);
                        }
                        else if (strcmp(onekey_line, "account_info_3") == 0)
                        {
                            if(line_id[3] == -1 || ACCOUNT3_REG != REGISTER_OK)
                            {
                                BARESIP_STATUS = IDLE;
                                break;
                            }
                            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",onekey_num, line_id[3]);
                        }
                        else if(strcmp(onekey_line,"p2p") == 0)
                        {
                            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s\"}",onekey_num);
                        }

                        if(DEBUG)
                            printf( "dial [%s]\n", cmd );
                        control_cmd(channel, cmd);
                        sprintf(strtmp, "%s <%s>", onekey_num, onekey_line);
                        writeLog( FUN, "KEY Call", strtmp );
                        break;
                    case RINGING:
                    case INUSE:
                        GetCmdValue(SPK_CONF, "intercom:repress_cancel", repress_cancel, sizeof(repress_cancel));
                        if(strcmp(repress_cancel, "yes") == 0){
                            strcpy(cmd,"{\"cmd\":\"hangup\",\"data\":\"\"}");
                            if(DEBUG)
                                printf( "hangup call\n" );
                            control_cmd(channel, cmd);
                            writeLog( FUN, "KEY Hangup", "" );
                        }
                        break;
                    case RING:
                        CURRENT_KEY = KEY;
                        strcpy(cmd,"{\"cmd\":\"accept\",\"data\":\"\"}");
                        if(DEBUG)
                            printf( "answer call\n" );
                        control_cmd(channel, cmd); 
                        writeLog( FUN, "KEY Answer", "" );
                        break;
                    case OFFLINE:
                        GetCmdValue(SPK_CONF, "call_action_url:unregistered_enable", enable, sizeof(enable));
                        if(strcmp(enable,"yes") == 0)
                        {
                            GetCmdValue(SPK_CONF, "call_action_url:unregistered_url", url, sizeof(url));
                            if(strstr(url,VAR_IP))
                            {
                                if(get_ip(ipaddr))
                                    strcpy(url, strrpl(url, VAR_IP, ipaddr));
                            }
                            if(strstr(url,VAR_MAC))
                            {
                                if(get_mac(macaddr))
                                    strcpy(url, strrpl(url, VAR_MAC, macaddr));
                            }
                            if(strstr(url,VAR_UA))
                            {
                                GetCmdValue(SPK_CONF, "intercom:onekey_line", onekey_line, sizeof(onekey_line));
                                sprintf(strtmp,"%s:username",onekey_line);
                                GetCmdValue(SPK_CONF, strtmp, account, sizeof(account));
                                strcpy(url, strrpl(url, VAR_UA, account));
                            }
                            if(strstr(url,VAR_NUM))
                            {
                                GetCmdValue(SPK_CONF, "intercom:onekey_num", onekey_num, sizeof(onekey_num));
                                strcpy(url, strrpl(url, VAR_NUM, onekey_num));
                            }
                            action_url(url);
                            writeLog(FUN, "HTTP Request", url);
                        }
                        break;
                }
            }
            break;
        case KEY2_10:
            system("/etc/scripts/play_rebooting.sh");
            system("rm -f /oem/.userdata && /sbin/reboot");
            break;
        case KEY2_3:
            PlayIP();
            break;
    }
}

void *checkReg()
{
	dictionary  *   ini ;

	ini = iniparser_load(SPK_CONF);
    if (ini==NULL) {
        fprintf(stderr, "cannot parse file: %s\n", SPK_CONF);
        return FALSE;
    }
	if((strcmp(iniparser_getstring(ini, "account_info_1:enable", "no"), "yes") == 0 || strcmp(iniparser_getstring(ini, "account_info_2:enable", "no"), "yes") == 0 ||
		strcmp(iniparser_getstring(ini, "account_info_3:enable", "no"), "yes") == 0) && regcount == 0)
	{
		system("/etc/scrtips/sipphone.sh stop");
	}
	regcount = 0;
	iniparser_freedict(ini);
}
                        
void reset_vlcInfo(int id)
{
    vlcInfo[id].pid = 0;
    vlcInfo[id].prio = 0;
    memset(vlcInfo->param, 0, sizeof(vlcInfo->param));
}

int file_exists(char *filename)
{
   return (access(filename, 0) == 0);
}

int vlc_cmd_action(char *action, int id)
{
    char cmd[128], ctrlfile[32];
    int i = 0;
    sprintf(ctrlfile,"/tmp/vlc_%d.sock",id);
    while(i < MAX_TRY)
    {
        if(file_exists(ctrlfile))
        {
            sprintf(cmd,"echo \"%s\" | socat - unix-connect:%s", action, ctrlfile);
            if(DEBUG) printf("volume CMD: %s\n", cmd);
            if(system(cmd) == 0)
                return TRUE;
            else
                continue;
        }
        usleep(200*1000);
        i ++;
    }
    return FALSE;
}

int schedule_start_check_prio(int prio)
{
    char cmd[64];
    for(int i = 1; i < 31; i++)
    {
        if(DEBUG)
            printf("==============%d,%d,%d,%s=============\n", i, SCHEDULE[i], vlcInfo[i].prio, vlcInfo[i].param);
        if(SCHEDULE[i] != STOP)
        {
            if(prio < vlcInfo[i].prio)
                return FALSE;
            else if(SCHEDULE[i] == RUNNING && prio > vlcInfo[i].prio)
            {
                if(vlc_cmd_action("pause",i))
                    SCHEDULE[i] = PAUSED;
            }
        }
    }
    return TRUE;
}

void schedule_stop_check_prio(int prio)
{
    char cmd[64];
    int paused[30],running[30],c_paused = 0, c_running = 0;
    int isRunning = FALSE;
    for(int i = 1; i < 31; i++)
    {
        if(SCHEDULE[i] == RUNNING)
        {
            return;
        }
        if(SCHEDULE[i] == PAUSED)
        {
            paused[c_paused] = i;
            c_paused ++;
        }
    }
    for(int p = prio; p > 0; p--)
    {
        for(int n = 0; n < c_paused; n++)
        {
            if(p == vlcInfo[paused[n]].prio)
            {
                state_event(AUDIO_ON);
                if(vlc_cmd_action("pause",paused[n]))
                {
                    SCHEDULE[paused[n]] = RUNNING;
                    isRunning = TRUE;
                }
            }
        }
        if(isRunning) return;
    }
    state_event(AUDIO_OFF);
}

void playAudio(int id, int times, int isBeep, char *enSrceen, char *file, int prio, int vol)
{
	char filetmp[64], action[128], sockio[32], paused[32],loop[16],cmd[64], gain[32];
	int status, isPaused;

    bzero(paused,sizeof(paused));
    if(!schedule_start_check_prio(prio))
    {
        strcpy(paused,"--start-paused");
        isPaused = TRUE;
    }
    else
    {
        strcpy(paused,"--no-start-paused");
        isPaused = FALSE;
        state_event(AUDIO_ON);
    }
	
	if(times == 0)
	{
        sprintf(filetmp, "/usr/share/baresip/%s", file);
        strcpy(loop,"--loop");
	}
	else
	{
		sprintf(filetmp,"/tmp/playlist_%d.m3u",id);
		FILE *fd = fopen(filetmp, "w+");
		if(fd == NULL)
		{
			if(DEBUG) printf("play failed!\n");
			return;
		}
		if(isBeep) fprintf(fd,"/usr/share/baresip/s_beep.mp3\n");
		for(int i=0;i<times;i++)
		{
			fprintf(fd,"/usr/share/baresip/%s\n",file);
		}
		if(isBeep) fprintf(fd,"/usr/share/baresip/e_beep.mp3\n");
		fclose(fd);
        strcpy(loop,"--no-loop");
	}
    sprintf(gain, "--gain=%.02f",  (float) vol/100);
    sprintf(sockio,"--rc-unix=/tmp/vlc_%d.sock", id);

    char *argv[] = {"vlc","--play-and-exit","--control","rc","--rc-fake-tty",sockio,paused,loop,gain,filetmp,NULL};
    status = posix_spawn(&vlcInfo[id].pid,"/usr/sbin/vlc",NULL,NULL,argv,NULL);
        
	if(status == 0)
	{
        usleep(300*1000);
        // sprintf(action,"volume %d", 256 * vol/100);
        // vlc_cmd_action(action,id);
        SCHEDULE[id] = (isPaused)?PAUSED:RUNNING;
        vlcInfo[id].prio = prio;
        sprintf(vlcInfo[id].param,"%s", file);
        sprintf(action,"Play audio: %s", vlcInfo[id].param);
        writeLog(FUN, "Audio Control", action);
        //system("echo 0 > /sys/class/gpio/gpio5/value");
		if(waitpid(vlcInfo[id].pid, &status, 0) != -1)
		{
			if(DEBUG) printf("Child exited with status %i\n", status);
            SCHEDULE[id] = STOP;
            schedule_stop_check_prio(vlcInfo[id].prio);
            reset_vlcInfo(id);
            if(strcmp(enSrceen, "Disabled") != 0)
            {
                sprintf(cmd,"{\"action\": \"off\", \"id\": %s}", enSrceen);
                screen_control(cmd);
            }
            //system("echo 1 > /sys/class/gpio/gpio5/value");
            sprintf(action,"Stop audio: %s", vlcInfo[id].param);
            writeLog(FUN, "Audio Control", action);
		}
	}
	if(times) remove(filetmp);
}

void *handle_playAudio(void *arg)
{
	int selfkey, isBeep = FALSE, times = 0;
	PlayInfo *p = arg;
    selfkey = p->key_id;
	if(strlen(p->times_str)) times = atoi(p->times_str);
	if(strcmp(p->enBeep, "yes") == 0) isBeep = TRUE;
	if(strlen(p->file))
	{
		playAudio(selfkey, times, isBeep, p->enSrceen, p->file, p->prio, p->vol);
	}
	free(p);
}

int equalDate(struct date A,struct date B)
{
	if(A.year==B.year && A.month==B.month && A.day==B.day)     
        return TRUE;
    return FALSE;
}

int compareDate(struct date A,struct date B)
{
	if(A.year<B.year)     
        return TRUE;
    if(A.year==B.year && A.month<B.month)
        return TRUE;
    if(A.year==B.year && A.month==B.month && A.day<B.day)
        return TRUE;
    return FALSE;
}

void date_format(char *date_str, struct date *s_holiday, struct date *e_holiday)
{
    struct date date;
    sscanf(date_str, "%d-%d-%d:%d-%d-%d", &s_holiday->year, &s_holiday->month, &s_holiday->day, &e_holiday->year, &e_holiday->month, &e_holiday->day);
}


int isHoliday(struct date currDate, char *holidays)
{
	cJSON *pJson, *pSub;
    struct date s_holiday, e_holiday;
	pJson = cJSON_Parse(holidays);
    if (pJson)
	{
		cJSON *holidaysArray = cJSON_GetObjectItem( pJson, "holidays");
		if(holidaysArray != NULL)
		{
			int array_size = cJSON_GetArraySize (holidaysArray);
			for(int n = 0; n < array_size; n++)
			{
				pSub = cJSON_GetArrayItem(holidaysArray, n);
                date_format(pSub->valuestring, &s_holiday, &e_holiday);
				if(DEBUG)
                {
                    printf("current date: %d-%d-%d\n",currDate.year, currDate.month, currDate.day);
                    printf("holiday start: %d-%d-%d\n",s_holiday.year, s_holiday.month, s_holiday.day);
                    printf("holiday end: %d-%d-%d\n",e_holiday.year, e_holiday.month, e_holiday.day);
                }
				if(equalDate(currDate, s_holiday) || (compareDate(s_holiday, currDate) && compareDate(currDate, e_holiday)))
				{
					cJSON_Delete(pJson);
					return TRUE;
				}
			}
		}
	}
	cJSON_Delete(pJson);
	return FALSE;
}

void schedule(int year, int month, int day, int hour, int minute, int week)
{
	char actiontype[32], keyaction[16], keyactiontype[32], type[16], action_type[16];
	char keystr[32],enable[8],weeks[8], enholiday[8], holidays[4096];
	char w[4],action[128],screen_id[16];
	int s_hour, e_hour, s_min, e_min;
	int s_time, e_time, time, interval;
	struct date s_date, e_date, date;
	dictionary  *   ini ;

	date.year = year;
	date.month = month;
	date.day = day;

	ini = iniparser_load(SCHEDULE_CONF);
    if (ini==NULL) {
        fprintf(stderr, "cannot parse file: %s\n", SCHEDULE_CONF);
        return;
    }
	strcpy(holidays, iniparser_getstring(ini, "holidays:data", "{\"holidays\": []}"));
	for(int i=1;i<31;i++)
	{
		sprintf(keystr,"schedule_%d:enable",i);
		strcpy(enable, iniparser_getstring(ini, keystr, "no"));
		if(strcmp(enable,"yes") == 0)
		{
			sprintf(keystr,"schedule_%d:enholiday",i);
			strcpy(enholiday, iniparser_getstring(ini, keystr, "no"));
			if(strcmp(enholiday, "yes") == 0 && isHoliday(date, holidays)) continue;

			sprintf(keystr,"schedule_%d:weeks",i);
			strcpy(weeks, iniparser_getstring(ini, keystr, ""));
			sprintf(w,"%d",week);
			if(strchr(weeks,w[0]) == NULL) continue;
			sprintf(keystr,"schedule_%d:s_year",i);
			s_date.year = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:e_year",i);
			e_date.year = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:s_month",i);
			s_date.month = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:e_month",i);
			e_date.month = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:s_day",i);
			s_date.day = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:e_day",i);
			e_date.day = iniparser_getint(ini, keystr, 0);
			if(compareDate(date, s_date) || compareDate(e_date, date)) continue;
			
			sprintf(keystr,"schedule_%d:s_hour",i);
			s_hour = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:e_hour",i);
			e_hour = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:s_min",i);
			s_min = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:e_min",i);
			e_min = iniparser_getint(ini, keystr, 0);
			sprintf(keystr,"schedule_%d:interval",i);
			interval = iniparser_getint(ini, keystr, 60);
			sprintf(keystr,"schedule_%d:trigger_screen",i);
			strcpy(screen_id, iniparser_getstring(ini, keystr, "Disabled"));
			time = hour * 60 + minute;
			s_time = s_hour * 60 + s_min;
			e_time = e_hour * 60 + e_min;

			if(time >= s_time && time <= e_time)
			{
				if(time == e_time)
				{
			        sprintf(keystr,"%d",i);
					writeLog(SCHED, keystr, "Executed");
					if(SCHEDULE[i] == RUNNING)
					{
						char strtmp[64],cmd[128];
                        if(strcmp(screen_id, "Disabled") != 0)
                        {
                            sprintf(strtmp,"{\"action\": \"off\", \"id\": %s}", screen_id);
                            screen_control(strtmp);
                        }
						sprintf(cmd,"kill -9 %d",vlcInfo[i].pid);
						system(cmd);
                        // system("echo 1 > /sys/class/gpio/gpio5/value");
                        // SCHEDULE[i] = STOP;
						// sprintf(action,"Stop audio: %s", vlcInfo[i].param);
						// writeLog(FUN, "Audio Control", action);
					}
					continue;
				}
				if((interval != 0 && (time - s_time) % interval == 0) || (interval == 0 && time == s_time))
				{
			        sprintf(keystr,"%d",i);
					writeLog(SCHED, keystr, "Executed");
                    char cmd[256], strtmp[64], source[16];
                    pthread_t thread_audio;
                    int ret_thrd;

                    if(SCHEDULE[i] == STOP)
                    {
                        PlayInfo *playAudioInfo = malloc(sizeof(PlayInfo));
                        playAudioInfo->key_id = i;
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp,"schedule_%d:source",i);
                        strcpy(source, iniparser_getstring(ini, strtmp, ""));
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp, "schedule_%d:audio_file", i);
                        strcpy(playAudioInfo->file, iniparser_getstring(ini, strtmp, ""));
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp,"schedule_%d:play_times",i);
                        strcpy(playAudioInfo->times_str, iniparser_getstring(ini, strtmp, ""));
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp,"schedule_%d:enBeep",i);
                        strcpy(playAudioInfo->enBeep, iniparser_getstring(ini, strtmp, "no"));
                        strcpy(playAudioInfo->enSrceen, screen_id);
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp,"schedule_%d:priority",i);
                        playAudioInfo->prio = iniparser_getint(ini, strtmp, 1);
                        bzero(strtmp, sizeof(strtmp));
                        sprintf(strtmp,"schedule_%d:volume",i);
                        playAudioInfo->vol = iniparser_getint(ini, strtmp, 100);
                        if(strcmp(screen_id, "Disabled") != 0)
                        {
                            sprintf(strtmp,"{\"action\": \"on\", \"id\": %s}", screen_id);
                            screen_control(strtmp);
                        }
                        ret_thrd = pthread_create(&thread_audio, NULL, handle_playAudio, playAudioInfo);
                        if (ret_thrd != 0)
                        {
                            printf("create thread1 handle_playAudio failed\n");
                            free(playAudioInfo);
                            continue;
                        }
                        pthread_detach(thread_audio);
                    }
                    else
                    {
                        if(strcmp(screen_id, "Disabled") != 0)
                        {
                            sprintf(strtmp,"{\"action\": \"off\", \"id\": %s}", screen_id);
                            screen_control(strtmp);
                        }
                        sprintf(cmd,"kill -9 %d",vlcInfo[i].pid);
                        system(cmd);
                        // system("echo 1 > /sys/class/gpio/gpio5/value");
                        // SCHEDULE[i] = STOP;
                        // sprintf(action,"Stop audio: %s", vlcInfo[i].param);
                        // writeLog(FUN, "Audio Control", action);
                    }
				}
			}
		}
	}
	iniparser_freedict(ini);
}

void *handleDateTime()
{
	time_t nSeconds;
	struct tm *pTM;
	int year, month, hour, count;
	pthread_t thread1;

	while(1)
	{
		time(&nSeconds);
		pTM = localtime(&nSeconds);

		if(pTM->tm_sec == 0)
		{
            // year = pTM->tm_year + 1900;
			// month = pTM->tm_mon + 1;
            // schedule(year, month, pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_wday);
			count ++;
			if(count > 120)
			{
				pthread_create(&thread1, NULL, checkReg, NULL);
                pthread_detach(thread1);
				count = 0;
			}
		}
		sleep(1);
	}
}


//解析音量设置数据
void ProcessVolumeReply( redisReply * pReply )
{
    cJSON *pJson = NULL;
    redisReply * pSubReply = NULL;
    char cmd[MAX_PIPE_BUFSIZE];
    int channel = BARESIP_CHAN;

    if ( pReply != NULL && pReply->elements == 3 )
    {
        pSubReply = pReply->element[2];
        pJson =  cJSON_Parse(pSubReply->str);
        if ( pJson ) {
            if(cJSON_GetObjectItem(pJson, "audio_volume"))
            {
                if(cJSON_GetObjectItem(pJson, "audio_volume")->valueint == 0)
                    schedule_start_check_prio(MAX_PRIORITY + 1);
                else
                    schedule_stop_check_prio(MAX_PRIORITY + 1);
                
            }
        }else{
            printf( "parse failed!\n");
        }
        if(pJson != NULL) cJSON_Delete(pJson);
    }
}


/*
* 订阅音量信息.
*/
void *get_volume_info()
{
    redisContext * pContext = redisConnectUnix(unix_socket_path);

    if ( NULL == pContext || pContext->err == 1 )
    {
        printf( "%s\n", pContext->errstr );
        exit( -1 );
    }

    char * pKey = "volume-value-channel";

    redisReply * pReply = redisCommand( pContext, "SUBSCRIBE %s", pKey );
    freeReplyObject( pReply );
    while ( redisGetReply( pContext, (void **)&pReply ) == REDIS_OK )
    {
        ProcessVolumeReply( pReply );
        freeReplyObject( pReply );
    }

    redisFree( pContext );
}

void play_audio(char *audiofile, int repeat)
{
    char cmd[320];
    int channel = BARESIP_CHAN;
    sprintf(cmd,"{\"cmd\":\"play\",\"data\":\"%s:%d\"}",audiofile,repeat);
    control_cmd(channel, cmd);
}

const int get_sip_status(char *accountaor)
{
    int status = OFFLINE;
    const int id = redis_pool_get_context(redisClient);
    if (id != -1) {
        redisReply *reply = NULL;
        reply = (redisReply *) redisCommand(redisClient->context[id], "GET %s", accountaor);
        if (reply && reply->type == REDIS_REPLY_STRING) {
            // 处理回复
            if(DEBUG) printf("SIP acc: %s, status: %s\n", accountaor, reply->str);
            if(strcmp(reply->str, "{\"status\":\"idle\"}") == 0)
            {
                status = IDLE;
            }
            else if(strcmp(reply->str, "{\"status\":\"answer\"}") == 0)
            {
                status = INUSE;
            }
            else if(strcmp(reply->str, "{\"status\":\"ringing\"}") == 0)
            {
                status = RINGING;
            }
            else if(strcmp(reply->str, "{\"status\":\"ring\"}") == 0)
            {
                status = RING;
            }
        }
        if(reply != NULL) freeReplyObject(reply);
        redis_pool_release_connection(redisClient, id);
    }
    if(BARESIP_STATUS == OFFLINE || BARESIP_STATUS == IDLE)
        BARESIP_STATUS = status;
    
    return status;
}

void sip_reg_status_init()
{
    //初始化SIP注册状态
    char enac1[8],enac2[8],enac3[8],p2penable[8],accountaor1[192], accountaor2[192], accountaor3[192];
    char account1[32], domain1[128], account2[32], domain2[128], account3[32], domain3[128];
    GetCmdValue(SPK_CONF, "account_info_1:enable", enac1, sizeof(enac1));
    if(strcmp(enac1, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "account_info_1:username", account1, sizeof(account1));
        GetCmdValue(SPK_CONF, "account_info_1:domain", domain1, sizeof(domain1));
        if(strlen(domain1) == 0)
        {
            GetCmdValue(SPK_CONF, "account_info_1:server", domain1, sizeof(domain1));
        }
        memset(accountaor1, '\0', sizeof(accountaor1));
        sprintf(accountaor1, "baresip-call-status-%s-%s", account1, domain1);
        if(get_sip_status(accountaor1) != OFFLINE)
        {
            ACCOUNT1_REG = REGISTER_OK;
        }
    }
    GetCmdValue(SPK_CONF, "account_info_2:enable", enac2, sizeof(enac2));
    if(strcmp(enac2, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "account_info_2:username", account2, sizeof(account2));
        GetCmdValue(SPK_CONF, "account_info_2:domain", domain2, sizeof(domain2));
        if(strlen(domain2) == 0)
        {
            GetCmdValue(SPK_CONF, "account_info_2:server", domain2, sizeof(domain2));
        }
        memset(accountaor2, '\0', sizeof(accountaor2));
        sprintf(accountaor2, "baresip-call-status-%s-%s", account2, domain2);
        if(get_sip_status(accountaor2) != OFFLINE)
        {
            ACCOUNT2_REG = REGISTER_OK;
        }
    }
    GetCmdValue(SPK_CONF, "account_info_3:enable", enac3, sizeof(enac3));
    if(strcmp(enac3, "yes") == 0)
    {
        GetCmdValue(SPK_CONF, "account_info_3:username", account3, sizeof(account3));
        GetCmdValue(SPK_CONF, "account_info_3:domain", domain3, sizeof(domain3));
        if(strlen(domain3) == 0)
        {
            GetCmdValue(SPK_CONF, "account_info_3:server", domain3, sizeof(domain3));
        }
        memset(accountaor3, '\0', sizeof(accountaor3));
        sprintf(accountaor3, "baresip-call-status-%s-%s", account3, domain3);
        if(get_sip_status(accountaor3) != OFFLINE)
        {
            ACCOUNT3_REG = REGISTER_OK;
        }
    }
    GetCmdValue(SPK_CONF, "account_info_p2p:enable", p2penable, sizeof(p2penable));
    if(strcmp(p2penable,"yes") == 0)
    {
        ACCOUNTP2P_REG = REGISTER_OK;
    }
    if(ACCOUNT1_REG == REGISTER_OK || ACCOUNT2_REG == REGISTER_OK || ACCOUNT3_REG == REGISTER_OK || ACCOUNTP2P_REG == REGISTER_OK)
    {

    }
}

const int init()
{
    int ret_thrd1,ret_thrd2,ret_thrd3,ret_thrd4;
    pthread_t thread1, thread2, thread3, thread4;

    redisClient = create_redis_pool_client(unix_socket_path);

    memset(SCHEDULE, 0,sizeof(SCHEDULE));
    
    sip_reg_status_init();

    //启动线程订阅BARESIP实时状态
    ret_thrd1 = pthread_create(&thread1, NULL, baresip_status, NULL);
    if (ret_thrd1 != 0) {
        printf("create thread1 baresip_status failed\n");
        return FALSE;
    }

    //启动日志记录线程
    ret_thrd2 = pthread_create(&thread2, NULL, write_opnlog, NULL);
    if (ret_thrd2 != 0) {
        printf("create thread2 write_opnlog failed\n");
        return FALSE;
    }

    //启动系统时间显示线程
    ret_thrd3 = pthread_create(&thread3, NULL, handleDateTime, NULL);
    if (ret_thrd3 != 0)
    {
        printf("create thread3 handleDateTime failed\n");
        return FALSE;
    }

    //启动线程订阅音量设置
    ret_thrd4 = pthread_create(&thread4, NULL, get_volume_info, NULL);
    if (ret_thrd4 != 0) {
        printf("create thread4 get_volume_info failed\n");
        return FALSE;
    }
    
    return TRUE;
}

int main(int argc, char *argv[]){
    struct pollfd fdset[2];
    int switch_fd,reset_fd,ret;
    int len, count, i, key1_action, key2_action;
    char buf[8], strtmp[64],action[8],url[256], srceen[8], type[16], input[8];
    time_t lastime, newtime;

    if(argc == 2 && strcmp(argv[1], "debug") == 0)
        DEBUG = TRUE;

    if(!init())
    {
        printf("init failed!\n");
        exit( -1 );
    }

    /* BUTTON */
    switch_fd = open("/sys/class/gpio/gpio111/value", O_RDONLY);
    read(switch_fd, buf, sizeof(buf));
    lseek(switch_fd, 0, SEEK_SET);
    /* RESET */
    reset_fd = open("/sys/class/gpio/gpio73/value", O_RDONLY);
    read(reset_fd, buf, sizeof(buf));
    lseek(reset_fd, 0, SEEK_SET);

    while(1)
    {
        memset(fdset, 0x00, sizeof(struct pollfd) * 2);
        fdset[0].fd = switch_fd;
        fdset[0].events = POLLPRI;
        fdset[1].fd = reset_fd;
        fdset[1].events = POLLPRI;

        ret = poll(fdset, 2, 3000);
        if(ret < 0){
            printf("npoll() faild! \n");
            return -1;
        }

        if(ret == 0)
            continue;
        /*BUTTON*/
        if( fdset[0].revents & POLLPRI )
        {
            count = 0;
            for(i=0;i<100;i++){ // 100ms
                bzero(buf,sizeof(buf));
                read(fdset[0].fd, buf, sizeof(buf));
                lseek(fdset[0].fd, 0, SEEK_SET);
                if(buf[0]-'0' == 0)
                    count++;
                usleep(1000);
            }
            // printf("zero counts: %d\n", count);
            if (count >= 70) {
                playBeep();
                if(DEBUG)
                    printf("KEY DOWN\n");
                writeLog(BTN, "KEY", "DOWN");
                GetCmdValue(SPK_CONF, "intercom:key_action", action, sizeof(action));
                if(strcmp(action,"call") == 0)
                {
                    keyAction(KEY);
                }
                if(strcmp(action,"http") == 0)
                {
                    GetCmdValue(SPK_CONF, "intercom:http_url", url, sizeof(url));
                    action_url(url);
	                writeLog(FUN, "KEY HTTP Request", url);
                }
                else if(strcmp(action,"play") == 0)
                {
                    char audiofile[32],repeat_tmp[16];
                    GetCmdValue(SPK_CONF, "intercom:audio_file", audiofile, sizeof(audiofile));
                    GetCmdValue(SPK_CONF, "intercom:audio_repeat", repeat_tmp, sizeof(repeat_tmp));
                    int repeat = atoi(repeat_tmp);
                    play_audio(audiofile,repeat);
                    sprintf(strtmp, "File: %s; Repeat: %d", audiofile, repeat);
                    writeLog( FUN, "KEY Play Auido", strtmp );
                }
            }
        }
        /*RESET*/
        if( fdset[1].revents & POLLPRI )
        {
            count = 0;
            for(i=0;i<100;i++){ // 100ms
                bzero(buf,sizeof(buf));
                read(fdset[1].fd, buf, sizeof(buf));
                lseek(fdset[1].fd, 0, SEEK_SET);
                if(buf[0]-'0' == 0)
                    count++;
                usleep(1000);
            }
            if (count >= 70) {
                time(&newtime);
                printf("KEY DOWN\n");
                writeLog(BTN, "RESET", "DOWN");
            } else {
                time(&lastime);
                len = lastime - newtime;
                printf("KEY UP, Time:%d\n", len);
                sprintf(strtmp, "Release <Duration %ds>", len);
                writeLog(BTN, "RESET", strtmp);

                if(len > 10)
                {
                    keyAction(KEY2_10);
                }
                else if(len > 3)
                {
                    keyAction(KEY2_3);
                }
            }
        }
    }
}
