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

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

//BARESIP_STATUS
#define IDLE        0
#define INUSE       1
#define RING        2
#define RINGING     3
#define HOLD        4
#define OFFLINE     5
#define DIALING     6
#define LOOPSTART   7
//BARESIP_REG
#define REGISTER_FAIL   0
#define REGISTER_OK     1
//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 unix_socket_path "/tmp/redis.sock"

//operator log type
#define BTN			"BUTTON"
#define FUN			"FUNCTION"
#define STA         "SIP STATE"
#define GPIO_RELAY  "gpio67"
#define VAR_IP      "${ip}"
#define VAR_MAC     "${mac}"
#define VAR_UA      "${ua}"
#define VAR_NUM     "${number}"

struct VLC_INFO{
    Boolean state;
	pid_t pid;
	char param[256];
};

typedef struct _playInfo{
	int duration;
	int volume;
	char url[128];
} PlayInfo;

typedef struct {
    redisContext *context;
    const char *socket_path;
    time_t last_activity;
} redis_client_t;

static int BARESIP_STATUS = OFFLINE;
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 PLAYING = FALSE;
static int delay = 0;

struct VLC_INFO vlcInfo;
static redis_client_t *redisClient;

redis_client_t* create_redis_client(const char *socket_path) {
    redis_client_t *client = malloc(sizeof(redis_client_t));
    client->socket_path = socket_path;
    client->context = redisConnectUnix(socket_path);
    client->last_activity = time(NULL);
    return client;
}

int ensure_connected(redis_client_t *client) {
    if (client->context == NULL || client->context->err) {
        if (client->context) {
            redisFree(client->context);
        }
        client->context = redisConnectUnix(client->socket_path);
        if (client->context == NULL || client->context->err) {
            return -1; // 连接失败
        }
    }
    client->last_activity = time(NULL);
    return 0;
}

void redis_command_safe(const char *format, char *value) {
    if (ensure_connected(redisClient) == 0) {
        redisReply *reply = redisCommand(redisClient->context, format, value);
        if (reply) {
            // 处理回复
            freeReplyObject(reply);
        }
    }
}

/*
*   @cmd 执行命令
*   @value 命令结果指针
*   @lenth 存储结果长度
int GetCmdValue(char *cmd, char *value, int lenth)
{
    bzero(value, lenth);
    FILE *fp = popen(cmd,"r");
    if(fp != NULL){
        fread(value, 1, lenth, fp);
        pclose(fp);
    } else {
        printf("Handle \"%s\" failed.\n", cmd);
        return FALSE;
    }
    return TRUE;
}
*/
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;
}

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);
}

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("/etc/scripts/pa_mute.sh 0");
    }
    else
    {
        system("/etc/scripts/pa_mute.sh 1");
    }
}

Boolean get_ip(char * pv)
{
        FILE * fp;
        char cmdbuf[128], value[32];
        char *tmp=NULL;
        sprintf(cmdbuf,"/sbin/ifconfig eth0 | grep inet | cut -d\":\" -f2 | cut -d\" \" -f1");
        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 eth0 | grep HWaddr | tr -d : | awk '{print $5}'");
        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;
}

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);
    writeLog( FUN, "API HTTP Request", url );
}

//发送控制指令
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 outputControl(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);
    }
}

//gpio延时控制
void *gpioDelayControl()
{
    usleep(delay*1000*1000);
    outputControl(GPIO_RELAY, GPIO_OFF);
    if(DEBUG)
        printf( "gpio135 set off\n" );
}

//呼入触发
void incomingTrigger(cJSON *pJson)
{
    char enable[8],url[256],ipaddr[16],macaddr[16], peeruri[128];
    GetCmdValue(SPK_CONF, "call_action_url:incoming_enable", enable, sizeof(enable));
    if(strcmp(enable,"yes") == 0)
    {
        GetCmdValue(SPK_CONF, "call_action_url:incoming_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))
        {
            strcpy(url, strrpl(url, VAR_UA, cJSON_GetObjectItem(pJson, "exten")->valuestring));
        }
        if(strstr(url,VAR_NUM))
        {
            char *ret, number[64];
            strcpy(peeruri, cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
            ret = strchr(peeruri, '@');
            if(ret)
            {
                memset(number,0,sizeof(number));
                strncpy(number,peeruri,ret - peeruri);
                strcpy(url, strrpl(url, VAR_NUM, number + 4));
            }
			
        }
        action_url(url);
    }
}

//呼出触发
void outgoingTrigger(cJSON *pJson)
{
    char enable[8],url[256],ipaddr[16],macaddr[16], peeruri[128];
    GetCmdValue(SPK_CONF, "call_action_url:outgoing_enable", enable, sizeof(enable));
    if(strcmp(enable,"yes") == 0)
    {
        GetCmdValue(SPK_CONF, "call_action_url:outgoing_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))
        {
            strcpy(url, strrpl(url, VAR_UA, cJSON_GetObjectItem(pJson, "exten")->valuestring));
        }
        if(strstr(url,VAR_NUM))
        {
            char *ret, number[64];
            strcpy(peeruri, cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
            ret = strchr(peeruri, '@');
            if(ret)
            {
                memset(number,0,sizeof(number));
                strncpy(number,peeruri,ret - peeruri);
                strcpy(url, strrpl(url, VAR_NUM, number + 4));
            }
        }
        action_url(url);
    }
}

//应答触发
void answeredTrigger(cJSON *pJson)
{
    char enable[8],url[256],ipaddr[16],macaddr[16], peeruri[128];
    GetCmdValue(SPK_CONF, "call_action_url:answered_enable", enable, sizeof(enable));
    if(strcmp(enable,"yes") == 0)
    {
        GetCmdValue(SPK_CONF, "call_action_url:answered_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))
        {
            strcpy(url, strrpl(url, VAR_UA, cJSON_GetObjectItem(pJson, "exten")->valuestring));
        }
        if(strstr(url,VAR_NUM))
        {
            char *ret, number[64];
            strcpy(peeruri, cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
            ret = strchr(peeruri, '@');
            if(ret)
            {
                memset(number,0,sizeof(number));
                strncpy(number,peeruri,ret - peeruri);
                strcpy(url, strrpl(url, VAR_NUM, number + 4));
            }
        }
        action_url(url);
    }
}

//应答触发
void hangupTrigger(cJSON *pJson)
{
    char enable[8],url[256],ipaddr[16],macaddr[16], peeruri[128];
    GetCmdValue(SPK_CONF, "call_action_url:hangup_enable", enable, sizeof(enable));
    if(strcmp(enable,"yes") == 0)
    {
        GetCmdValue(SPK_CONF, "call_action_url:hangup_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))
        {
            strcpy(url, strrpl(url, VAR_UA, cJSON_GetObjectItem(pJson, "exten")->valuestring));
        }
        if(strstr(url,VAR_NUM))
        {
            char *ret, number[64];
            strcpy(peeruri, cJSON_GetObjectItem(pJson, "peeruri")->valuestring);
            ret = strchr(peeruri, '@');
            if(ret)
            {
                memset(number,0,sizeof(number));
                strncpy(number,peeruri,ret - peeruri);
                strcpy(url, strrpl(url, VAR_NUM, number + 4));
            }
        }
        action_url(url);
    }
}

//baresip状态监控
void ProcessReply( redisReply * pReply )
{
    cJSON *pJson = NULL;
    redisReply * pSubReply = NULL;

    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, "type")->valuestring, "CALL_ESTABLISHED") == 0)
            {
                //通话
                BARESIP_STATUS = INUSE;
                answeredTrigger(pJson);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "incoming") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_CLOSED") == 0)
            {
                //挂断
                if(BARESIP_STATUS != OFFLINE){
                    BARESIP_STATUS = IDLE;
                }
                hangupTrigger(pJson);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "outgoing") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_CLOSED") == 0)
            {
                //挂断
                if(BARESIP_STATUS != OFFLINE){
                    BARESIP_STATUS = IDLE;
                }
                hangupTrigger(pJson);
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "incoming") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_INCOMING") == 0)
            {
                //呼入振铃状态
                if(BARESIP_STATUS == IDLE)
                {
                    BARESIP_STATUS = RING;
                    incomingTrigger(pJson);
                }
            }
            else if(strcmp(cJSON_GetObjectItem(pJson, "direction")->valuestring, "outgoing") == 0 &&
                strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "CALL_OUTGOING") == 0)
            {
                //呼出回铃状态
                if(BARESIP_STATUS == IDLE || BARESIP_STATUS == DIALING)
                {
                    BARESIP_STATUS = RINGING;
                    outgoingTrigger(pJson);
                }
            }
            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)
                {
                    BARESIP_STATUS = IDLE;
                }
				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);
							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);
							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);
							return;
						}
					}
				}
            }
            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;
                            }
							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;
                            }
							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;
                            }
							cJSON_Delete(pJson);
							return;
						}
					}
				}
            }
            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;
            }
            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)
                    {
                        ACCOUNTP2P_REG = REGISTER_OK;
                        BARESIP_STATUS = IDLE;
                    }
                    else
                    {
                        ACCOUNTP2P_REG = REGISTER_FAIL;
                    }
                }
            }
            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 sipphone_call(char *number, char *line)
{
    char cmd[MAX_PIPE_BUFSIZE];
    char accountaor[192],strtmp[64];
    char account[32],domain[128];
    int channel = BARESIP_CHAN, line_id[4];
    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    if(BARESIP_STATUS == IDLE)
    {
        BARESIP_STATUS = DIALING;
        if(strlen(number) == 0) 
        {
            BARESIP_STATUS = IDLE;
            return;
        }
        get_line(line_id);
        if(strcmp(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;
                    return;
                }
                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[1]);
            }
            else if(ACCOUNT2_REG == REGISTER_OK)
            {
                if(line_id[2] == -1)
                {
                    BARESIP_STATUS = IDLE;
                    return;
                }
                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[2]);
            }
            else if(ACCOUNT3_REG == REGISTER_OK)
            {
                if(line_id[3] == -1) 
                {
                    BARESIP_STATUS = IDLE;
                    return;
                }
                sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[3]);
            }
            else
            {
                BARESIP_STATUS = IDLE;
                return;
            }
        }
        else if (strcmp(line, "account_info_1") == 0)
        {
            if(line_id[1] == -1 || ACCOUNT1_REG != REGISTER_OK)
            {
                BARESIP_STATUS = IDLE;
                return;
            }
            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[1]);
        }
        else if (strcmp(line, "account_info_2") == 0)
        {
            if(line_id[2] == -1 || ACCOUNT2_REG != REGISTER_OK)
            {
                BARESIP_STATUS = IDLE;
                return;
            }
            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[2]);
        }
        else if (strcmp(line, "account_info_3") == 0)
        {
            if(line_id[3] == -1 || ACCOUNT3_REG != REGISTER_OK) 
            {
                BARESIP_STATUS = IDLE;
                return;
            }
            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s %d\"}",number, line_id[3]);
        }
        else if(strcmp(line,"p2p") == 0)
        {
            sprintf(cmd,"{\"cmd\":\"dial\",\"data\":\"%s\"}",number);
        }
        
        if(DEBUG)
            printf( "dial [%s]\n", cmd );
        control_cmd(channel, cmd);
        sprintf(strtmp, "%s <%s>", number, line);
        writeLog( FUN, "API Call", strtmp );
    }
}

void sipphone_answer()
{
    char cmd[MAX_PIPE_BUFSIZE];
    int channel = BARESIP_CHAN;
    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    if(BARESIP_STATUS == RING)
    {
        strcpy(cmd,"{\"cmd\":\"accept\",\"data\":\"\"}");
        if(DEBUG)
            printf( "answer call\n" );
        control_cmd(channel, cmd);
        writeLog( FUN, "API Answer", "" );
    }
}

void sipphone_hangup()
{
    char cmd[MAX_PIPE_BUFSIZE];
    int channel = BARESIP_CHAN;
    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    if(BARESIP_STATUS == RINGING || BARESIP_STATUS == INUSE)
    {
        strcpy(cmd,"{\"cmd\":\"hangup\",\"data\":\"\"}");
        if(DEBUG)
            printf( "hangup call\n" );
        control_cmd(channel, cmd);
        writeLog( FUN, "API Hangup", "" );
    }
}

void sipphone_loop_start()
{
    char cmd[MAX_PIPE_BUFSIZE], hversion[16];
    int channel = BARESIP_CHAN;
    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    if(BARESIP_STATUS == IDLE || BARESIP_STATUS == OFFLINE)
    {
        strcpy(cmd,"{\"cmd\":\"auloop\",\"data\":\"48000 1\"}");
        if(DEBUG)
            printf( "audio loop\n" );
        control_cmd(channel, cmd);
        GetCmdValue(SPK_CONF, "system:hard_version", hversion, sizeof(hversion));
        if(strcmp(hversion, "Ver3.2") == 0)
            system("/etc/scripts/pa_mute.sh 0;echo 0 > /sys/class/gpio/gpio143/value");
        else
            system("/etc/scripts/pa_mute.sh 0;echo 0 > /sys/class/gpio/gpio134/value");
        BARESIP_STATUS = LOOPSTART;
        writeLog( FUN, "API Audio Loop", "Start" );
    }
}

void sipphone_loop_stop()
{
    char cmd[MAX_PIPE_BUFSIZE], hversion[16];
    int channel = BARESIP_CHAN;
    if(DEBUG)
        printf( "BARESIP_STATUS [%d]\n", BARESIP_STATUS );

    if(BARESIP_STATUS == LOOPSTART)
    {
        strcpy(cmd,"{\"cmd\":\"auloop_stop\",\"data\":\"\"}");
        if(DEBUG)
            printf( "hangup call\n" );
        control_cmd(channel, cmd);
        GetCmdValue(SPK_CONF, "system:hard_version", hversion, sizeof(hversion));
        if(strcmp(hversion, "Ver3.2") == 0)
            system("/etc/scripts/pa_mute.sh 1;echo 1 > /sys/class/gpio/gpio143/value");
        else
            system("/etc/scripts/pa_mute.sh 1;echo 1 > /sys/class/gpio/gpio134/value");
        BARESIP_STATUS = IDLE;
        writeLog( FUN, "API Audio Loop", "Stop" );
    }
}

void *playTimer(void *arg)
{
    char cmd[32];
    PlayInfo *p = arg;
    long t = p->duration*1000*1000;
    usleep(t);
    vlcInfo.state = FALSE;
    sprintf(cmd,"kill -9 %d",vlcInfo.pid);
	system(cmd);
    writeLog(FUN, "Specified duration expires", vlcInfo.param);
}

void *handle_playAudio(void *arg)
{
    char syscmd[256];
    int status;
	PlayInfo *p = arg;
	if(strlen(p->url) > 7 && (strncmp(p->url, "http://", 7) == 0 || strncmp(p->url, "rtsp://", 7) == 0 || strncmp(p->url, "ftp://", 6) == 0))
    {
        if(p->volume >= 0)
        {
            sprintf(syscmd, "vlc -I dummy --network-caching=100 --gain=%.02f --play-and-exit '%s'", (float) p->volume/100, p->url);
            sprintf(vlcInfo.param,"URL: %s; valume: %d", p->url, p->volume);
        }
        else
        {
            sprintf(syscmd, "vlc -I dummy --network-caching=100 --gain=%.02f --play-and-exit '%s'", (float) p->volume/100, p->url);
            sprintf(vlcInfo.param,"URL: %s", p->url);
        }
        char *argv[] = {"sh","-c",syscmd, NULL};
        status = posix_spawn(&vlcInfo.pid,"/bin/sh",NULL,NULL,argv,NULL);
        if(status == 0)
        {
            vlcInfo.state = TRUE;
            amplifier_switch(TRUE);
            if(p->duration > 0)
            {
                pthread_t thread_audio;
                char strtmp[32];
                pthread_create(&thread_audio, NULL, playTimer, p);
                pthread_detach(thread_audio);
                sprintf(strtmp,"; duration: %ds", p->duration);
                strcat(vlcInfo.param, strtmp);
            }
            writeLog(FUN,"API Play URL Audio", vlcInfo.param);
            if(waitpid(vlcInfo.pid, &status, 0) != -1)
            {
                amplifier_switch(FALSE);
                printf("Child exited with status %i\n", status);
                if(vlcInfo.state)
                {
                    writeLog(FUN, "Audio file playback ends", vlcInfo.param);
                    vlcInfo.state = FALSE;
                }
            }
        }
    }
	free(p);
}

//gpio控制指令处理
void ProcessAPIReply( redisReply * pReply )
{
    cJSON *pJson = NULL, *subJson = NULL;
    redisReply * pSubReply = NULL;
    char cmd[MAX_PIPE_BUFSIZE], shellcmd[MAX_PIPE_BUFSIZE];
    char sipphone[8],relay[8],player[8],strtmp[64];
    int channel = BARESIP_CHAN;

    if ( pReply->elements == 2 )
    {
        pSubReply = pReply->element[1];
        if(DEBUG)
            printf( "Msg [%s]\n", pSubReply->str );
        pJson =  cJSON_Parse(pSubReply->str);
        if ( pJson ) {
            if(cJSON_GetObjectItem(pJson, "type"))
            {
                if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "sipphone") == 0)
                {
                    GetCmdValue(SPK_CONF, "api:call_enable", sipphone, sizeof(sipphone));
                    if(cJSON_GetObjectItem(pJson, "action")){
                        if(strcmp(sipphone, "yes") == 0)
                        {
                            if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "call") == 0)
                            {
                                if(cJSON_GetObjectItem(pJson, "data"))
                                {
                                    subJson = cJSON_GetObjectItem(pJson, "data");
                                    sipphone_call(cJSON_GetObjectItem(subJson, "number")->valuestring, cJSON_GetObjectItem(subJson, "line")->valuestring);
                                }
                            }
                            else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "answer") == 0)
                            {
                                sipphone_answer();
                            }
                            else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "hangup") == 0)
                            {
                                sipphone_hangup();
                            }
                            
                        }
                        if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "auloopstart") == 0)
                        {
                            sipphone_loop_start();
                        }
                        else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "auloopstop") == 0)
                        {
                            sipphone_loop_stop();
                        }
                    }
                }
                else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "relay") == 0)
                {
                    if(cJSON_GetObjectItem(pJson, "action"))
                    {
                        if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "on") == 0)
                        {
                            outputControl(GPIO_RELAY, GPIO_ON);
                            subJson = cJSON_GetObjectItem(pJson, "data");
                            if(subJson){
                                delay = cJSON_GetObjectItem(subJson, "duration")->valueint;
                                if(delay > 0)
                                {
                                    int ret_thrd_delay;
                                    pthread_t thread_delay;
                                    ret_thrd_delay = pthread_create(&thread_delay, NULL, gpioDelayControl, NULL);
                                    if (ret_thrd_delay != 0) {
                                        printf("create thread_delay gpioDelayControl failed\n");
                                        cJSON_Delete(pJson);
                                        return;
                                    }
                                    pthread_detach(thread_delay);
                                }
                            }
                        }
                        else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "off") == 0)
                        {
                            outputControl(GPIO_RELAY, GPIO_OFF);
                        }
                    }
                }
                else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "player") == 0)
                {
                    if(cJSON_GetObjectItem(pJson, "action"))
                    {
                        if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "start") == 0)
                        {
                            subJson = cJSON_GetObjectItem(pJson, "data");
                            if(subJson && cJSON_GetObjectItem(subJson, "repeat") && cJSON_GetObjectItem(subJson, "volume"))
                            {
                                int repeat = cJSON_GetObjectItem(subJson, "repeat")->valueint;
                                int volume = cJSON_GetObjectItem(subJson, "volume")->valueint;
                                if(volume >= 0)
                                {
                                    sprintf(cmd,"{\"cmd\":\"setaudiotempvol\",\"data\":\"%d\"}",volume);
                                    control_cmd(channel, cmd);
                                }
                                sprintf(cmd,"{\"cmd\":\"play\",\"data\":\"%s:%d\"}",cJSON_GetObjectItem(subJson, "file")->valuestring,repeat);
                                control_cmd(channel, cmd);
                                PLAYING = TRUE;
                                sprintf(strtmp, "File: %s; Repeat: %d; Volume: %d", cJSON_GetObjectItem(subJson, "file")->valuestring, repeat, volume);
                                writeLog( FUN, "API Play Auido", strtmp );
                            }
                        }
                        else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "stop") == 0)
                        {
                            sprintf(cmd,"{\"cmd\":\"play\",\"data\":\"\"}");
                            control_cmd(channel, cmd);
                            PLAYING = FALSE;
                            writeLog( FUN, "API Stop Auido", "" );
                        }
                    }
                }
                else if(strcmp(cJSON_GetObjectItem(pJson, "type")->valuestring, "urlplayer") == 0)
                {
                    if(cJSON_GetObjectItem(pJson, "action"))
                    {
                        if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "start") == 0)
                        {
                            subJson = cJSON_GetObjectItem(pJson, "data");
                            if(subJson && cJSON_GetObjectItem(subJson, "duration") && cJSON_GetObjectItem(subJson, "volume"))
                            {
                                pthread_t thread_audio;
	                            int ret_thrd;
                                PlayInfo *playAudioInfo = malloc(sizeof(PlayInfo));
                                playAudioInfo->duration = cJSON_GetObjectItem(subJson, "duration")->valueint;
                                playAudioInfo->volume = cJSON_GetObjectItem(subJson, "volume")->valueint;
                                strcpy(playAudioInfo->url, cJSON_GetObjectItem(subJson, "url")->valuestring);

                                ret_thrd = pthread_create(&thread_audio, NULL, handle_playAudio, playAudioInfo);
                                if (ret_thrd != 0)
                                {
                                    printf("create thread1 handle_playAudio failed\n");
                                    free(playAudioInfo);
                                    cJSON_Delete(pJson);
                                    return;
                                }
                                pthread_detach(thread_audio);
                            }
                        }
                        else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "stop") == 0)
                        {
                            if(vlcInfo.state)
                            {
                                sprintf(cmd,"kill -9 %d",vlcInfo.pid);
                                system(cmd);
                                vlcInfo.state = FALSE;
                                writeLog(FUN, "Playback stops by API call", vlcInfo.param);
                            }
                        }
                    }
                }
            }
        }else{
            printf( "parse failed!\n");
        }
        if(pJson != NULL) cJSON_Delete(pJson);
    }
}

/*
* 接收控制指令.
*/
void *api_control()
{
    redisContext * pContext = redisConnectUnix(unix_socket_path);

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

    char * pKey = "api-channel";
    redisReply * pReply;
    while(1){
        pReply = NULL;
        pReply = redisCommand( pContext, "BRPOP %s 60", pKey );
		if(pReply != NULL && pReply->type == REDIS_REPLY_ARRAY){
			ProcessAPIReply( pReply );
		}
		if(pReply != NULL) freeReplyObject( pReply );
    }
    redisFree( pContext );
}


//解析音量设置数据
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, "sip_volume"))
            {
                sprintf(cmd,"{\"cmd\":\"setvol\",\"data\":\"%d\"}",cJSON_GetObjectItem(pJson, "sip_volume")->valueint);
                control_cmd(channel, cmd);
            }
        }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 );
}

int main(int argc, char *argv[]){
    char model[16];
    int ret_thrd1,ret_thrd2;
    pthread_t thread1, thread2;

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

    redisClient = create_redis_client(unix_socket_path);

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

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