/* Includes ------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* 标准C库头文件. */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>

#include "cmd.h"
#include "mtimer.h"
#include "process.h"
#include "hwgpio.h"
#include "fifo.h"
#include "ca_dbg.h"
#include "ca_csg.h"
#include "ca_cau.h"
#include "ca_param.h"
#include "ca_network.h"
#include "debug.h"
#include "common.h"
extern int csg_reconn_times;
extern int mqtt_reconn_times;

/* Private define ------------------------------------------------------------*/
int _network_chat(int fd, const char *at, const char *expect, int timeout, char **response, bool IsPrintf)
{
    int ret;
    int read_len = 0;
    static char buf[512];
    int errNo = 0;

    if (response)
    {
        *response = NULL;
    }

    tcflush(fd, TCIOFLUSH);

    if (IsPrintf == 1)
    {
        LOG("chat --> %s", at);
    }

    do
    {
        ret = write(fd, at, strlen(at));
    }
    while (ret < 0 && errno == EINTR);

    if (ret <= 0)
    {
        if (IsPrintf == 1)
        {
            errNo = errno;
            LOG("chat write error on stdout: %s(%d) ", strerror(errNo), errNo);
        }
        return errno ? errno : EINVAL;
    }

    struct pollfd poll_fd = {fd, POLLIN, 0};
    if (poll(&poll_fd, 1, timeout > 600 ? timeout : 600) <= 0)
    {
        if (errno != EINTR)
        {
            if (IsPrintf == 1)
            {
                errNo = errno;
                LOG("chat poll error on stdin:[%d][%s] %s(%d) ", ret, at, strerror(errNo), errNo);
            }
            return errno ? errno : EINVAL;
        }
    }
    if (poll_fd.revents && (poll_fd.revents & POLLIN))
    {
        memset(buf, 0, sizeof(buf));
        usleep(100 * 1000);
        if ((read_len = read(fd, buf, sizeof(buf) - 1)) <= 0)
        {
            if (IsPrintf == 1)
            {
                errNo = errno;
                LOG("chat read error on stdin: %s(%d) ", strerror(errNo), errNo);
            }
            return errno ? errno : EINVAL;
        }

        if (IsPrintf == 1)
        {
            LOG("\nchat return ret=[%d] at=[%s]string.len = [%d] <----- [%s];expect:[%s]\n", ret, at, strlen(buf), buf, expect);
        }

        if (read_len >= 512)
        {
            LOG("chat read max len [read:%d--max:%d]\n", read_len, 512);
            return -1;
        }

        if (strstr(buf, expect))
        {

            if (response)
            {
                *response = strstr(buf, expect);
            }
            return 0;
        }
        else
        {
            if (response)
            {
                *response = buf;
            }
        }
    }

    return errno ? errno : EINVAL;
}

int _network_AT_read_operation(const char *Devname, const char *cmd, \
                         const char *tag, char *response, int timeout)
{
    int modem_fd = -1, fdflags;
    struct termios  ios;
    int modembits = TIOCM_DTR;
    char *TmpResponse;
    int ret = 0;

    if (Devname == NULL || response == NULL || tag == NULL)
    {
        return 3;
    }

    pthread_mutex_lock(&network_ctrl.m_mutex);
    
    modem_fd = open (Devname, O_RDWR | O_NONBLOCK);
    if (modem_fd == -1)
    {
        LOG("open AT Dev[%s] error\n", Devname);
        pthread_mutex_unlock(&network_ctrl.m_mutex);
        return 1;
    }

    fdflags = fcntl(modem_fd, F_GETFL);
    if (fdflags != -1)
    {
        fcntl(modem_fd, F_SETFL, fdflags | O_NONBLOCK);
    }
    tcgetattr( modem_fd, &ios );
    cfmakeraw(&ios);
    ios.c_lflag = 0;
    cfsetispeed(&ios, B115200);
    cfsetospeed(&ios, B115200);
    tcsetattr( modem_fd, TCSANOW, &ios );
    ioctl(modem_fd, (0 ? TIOCMBIS : TIOCMBIC), &modembits);

    ret = _network_chat(modem_fd, cmd, tag, timeout, &TmpResponse, 1);
    if (0 != ret)
    {
        LOG("AT Error!!ret=%s, AT:[%s]----->response:[%s].", strerror(ret), cmd, TmpResponse);
        close(modem_fd);
        pthread_mutex_unlock(&network_ctrl.m_mutex);
        return 2;
    }
    //_MSG("TmpResponse :%s, len = %d\n", TmpResponse, strlen(TmpResponse));
    memcpy(response, TmpResponse, sizeof(char) * 512); 

    pthread_mutex_unlock(&network_ctrl.m_mutex);
    return 0;
}

int _network_change_apn(const char *apn)
{
    int i=0,num;
    char str[100][256]={{0}};
    char linedata[256]={0};
    FILE *fp=fopen("/etc/ppp/peers/quectel-chat-connect","r");
    if(NULL == fp)
    {
        LOG("fopen failed [%d:%s]", errno, strerror(errno));
        return -1;
    }

    while (fgets(linedata,sizeof(linedata)-1,fp))
    {
        if (strstr(linedata,"AT+CGDCONT")!=NULL)
        {
            sprintf(str[i],"OK \\rAT+CGDCONT=1,\"IP\",\"%s\",,0,0\n",apn);
        }
        else
        {
            strcpy(str[i],linedata);
        }
        i++;
    }
    fclose(fp);

    num=i;
    FILE * fpw = fopen("/etc/ppp/peers/quectel-chat-connect","w");
    for (i=0;i<num;i++)
    {
        fputs(str[i],fpw);
    }
    fflush(fpw);
    fclose(fpw);
    return 0;
}

void _network_usb_4g_power_on(void)
{
    LOG("\n");
    GPIO_USB_4G_PWR(ON);
    GPIO_USB_4G_WAKEUP(ON);
    sleep(5);
}

void _network_usb_4g_reset(void)
{
/* RST
    ____                     ___   ____ 
        |                   |
        |                   |
        |-------------------|
        |<---150ms~460ms--->|
*
 */

    LOG("\n");
    GPIO_USB_4G_RST(1); // low
    usleep(350*1000);
    GPIO_USB_4G_RST(0); // high
    sleep(30);
}

void _network_quectel_exec(void)
{
    char cmd[250] = {0};
    strcpy(cmd, "pppd call quectel-ppp &");
    LOG("%s", cmd);
    system(cmd);
    sleep(20);
}

void _network_kill_ppp()
{
    char cmd[250] = {0};
    strcpy(cmd, "killall pppd");
    LOG("%s", cmd);
    system(cmd);
}

int _network_check_usb_exist(const char* iDevPath)
{
    int iReturnCode = 0;
    struct stat tmpstat;
    if (NULL == iDevPath)
    {
        iReturnCode = 1;
    }
    else
    {
        if (stat(iDevPath, &tmpstat) != 0)
        {
            LOG("4G module USB not exist!\n");
            iReturnCode = 1;
        }
    }

    return iReturnCode;
}

int _network_ppp_check(void)
{
    char cmd[100];
    FILE *Read_fp;
    int chars_read;
    char buffer[1000];
    static int ResetEC20Cnt = 0;   //重启次数
    memset(buffer,0,1000);
    memset(cmd,0,100);
    sprintf(cmd,"ifconfig | grep ppp0");
    Read_fp = popen(cmd,"r");
    if(Read_fp != NULL)
    {
        chars_read = fread(buffer,sizeof(char),1000-1,Read_fp);
        pclose(Read_fp);

        if(chars_read > 0)
        {
            ResetEC20Cnt = 0;
            return 1;
        }
        else
        {
            ResetEC20Cnt++;
            if(ResetEC20Cnt > 6)
            {
                ResetEC20Cnt = 0;
                reboot_system(LOG_NETWORK, BOOT_PPP_NO_FOUND);
            }

            system("killall pppd");
            _network_usb_4g_power_on();
            _network_usb_4g_reset();
            LOG("ppp0:No Found.EC20 掉线");
            _network_quectel_exec();
        }
    }
    return 0;
}


int _network_ppp_check_test(void)
{
    char cmd[100];
    FILE *Read_fp;
    int chars_read;
    char buffer[1000];
    memset(buffer,0,sizeof(buffer));
    memset(cmd,0,sizeof(cmd));
    sprintf(cmd,"ifconfig | grep ppp0");
    Read_fp = popen(cmd,"r");
    if(Read_fp != NULL)
    {
        chars_read = fread(buffer,sizeof(char),1000-1,Read_fp);
        pclose(Read_fp);

        if(chars_read > 0)
        {
            //LOG("ppp0 success");
            return 1;
        }
    }
    LOG("not found ppp0");
    return 0;
}


void _network_connect_4g()
{
    int iReturnCode = -1;
    char response[512] = {0};
    if (network_ctrl.net_dev_stat == PPP_ON)
    {
        //if (_network_get_module_type() == WCDMA_EC20)
        //{
        //    ret_code = _network_AT_read_operation(DEV_NAME_4G_QUECTEL, "AT+CSQ\r\n", "+CSQ:", response);
        //}
        if (_network_check_usb_exist(DEV_NAME_4G_QUECTEL) != 0)
        {
            network_ctrl.net_dev_stat = PPP_OFF;
        }
        //iReturnCode = _network_AT_read_operation(DEV_NAME_4G_QUECTEL, "AT+CSQ\r\n", "+CSQ:", response, 0);
        //LOG("at return=%d", iReturnCode);
        //LOG("response=%s", response);
    }
    else
    {
        //_network_usb_4g_reset();
        //sleep(20);
        _network_quectel_exec();
        network_ctrl.net_dev_stat = PPP_ON;
        sleep(20);
    }
}

void _network_recovery_dial_file()
{
    int i, num;
    char src[128] = {0};
    char cmd[256] = {0};
    char file_path[128] = {0};
    char file_name[][128] = 
    {
        "quectel-chat-connect",
        "quectel-chat-disconnect",
        "quectel-dial",
        "quectel-ppp"
    };

    num = sizeof(file_name)/sizeof(file_name[0]);
    //LOG("num=%d\n", num);
    for (i = 0; i < num; i++)
    {
        snprintf(file_path, 128, "%s/%s", DIAL_PPP_PATH, file_name[i]);

        if (access(file_path, F_OK))
        {
            snprintf(src, 128, "%s/%s", DIAL_PPP_BAK_PATH, file_name[i]);
            if (access(src, F_OK) == 0)
            {
                snprintf(cmd, 256, "cp -rf %s %s", src, DIAL_PPP_PATH);    
                system(cmd);
                LOG("exec:%s\n", cmd);
                usleep(100*1000);
            }
        }
    }
}

void *_network_manage_thread(void *args)
{
    //_network_connect_4g();
    
    int reset_cnt = 0;
    int ppp_not_found_cnt = 0;
    int start_4g_flag = 0;
   
    while (1)
    {
        if (pparam_config->gateway.radioType != 1)
        {
            DBG(DBG_M_CA_NETWORK, "radioType:%d", pparam_config->gateway.radioType);
            sleep(1);
            continue;
        }
        
        if (start_4g_flag == 0)
        {
            start_4g_flag = 1;
            _network_usb_4g_power_on();
            LOG("4G module power on");
            sleep(15);
             _network_quectel_exec();
        }
        
        if (access(DEV_NAME_4G_QUECTEL, F_OK) == 0)
        {
            reset_cnt = 0;
            if (_network_ppp_check_test() == 0)
            {
                // not found ppp0
                if (++ppp_not_found_cnt >= 6)
                {
                    LOG("ppp0 not found > %d times, should reboot.", ppp_not_found_cnt);
                    ppp_not_found_cnt = 0;
                    reboot_system(LOG_NETWORK, BOOT_PPP_NO_FOUND);
                   
                }
                _network_kill_ppp();
                _network_usb_4g_power_on();
                _network_usb_4g_reset();
                _network_quectel_exec();
            }
            else
            {
                // found ppp0
                ppp_not_found_cnt = 0;
                // The connection still fails in the presence of ppp0 and needs to be restarted
                if (pparam_config->gateway.CommunicationMode == 2)
                {
                    if (mqtt_reconn_times > 6)
                    {
                        reboot_system(LOG_NETWORK, BOOT_PPP_FOUND_MQTT_LINK_FAIL);
                    }
                }
                else
                {
                    if (csg_reconn_times > 10)
                    {
                        reboot_system(LOG_NETWORK, BOOT_PPP_FOUND_CSG_LINK_FAIL);
                    }
                }
            }
        }
        else
        {
            ppp_not_found_cnt = 0;
            LOG("%s is not exist!!!! reset_cnt = %d\n", DEV_NAME_4G_QUECTEL, reset_cnt);
            if (++reset_cnt >= 6)
            {
                reset_cnt = 0;
                reboot_system(LOG_NETWORK, BOOT_TTYUSB_NO_FOUND);
            }

            _network_usb_4g_power_on();
            _network_usb_4g_reset();
            _network_quectel_exec();
        }
        _network_recovery_dial_file();
        sleep(1);
    }
}

int _network_handle_init_common()
{
    struct sched_param param;
    pthread_attr_t attr;
    pthread_t pid;
    int32_t rv = 0;
    
    /* 配置线程RR调度, 优先级72 */
    pthread_attr_init(&attr);
    param.sched_priority = 76;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _network_manage_thread, NULL);
    if (rv != 0)
    {
        log_err(LOG_NETWORK, "PD can't create _network_manage_thread %d!", rv);
        return E_ERROR;
    }
    else
    {
        thread_m_add("CA_NETWORK", pid);
    }
    pthread_attr_destroy(&attr);

    return E_NONE;
}


/* Interface functions -------------------------------------------------------*/
/* 4G模块初始化. */
int network_handle_init(void)
{
    //_network_usb_4g_power_on();
    //LOG("4G module power on");
    //sleep(15);
    memset(&network_ctrl, 0, sizeof(network_ctrl));
    network_ctrl.net_dev_stat = PPP_OFF;
    pthread_mutex_init(&network_ctrl.m_mutex, NULL);
    /* 初始化模块. */
    LD_E_RETURN(DBG_M_CA_NETWORK_ERR, _network_handle_init_common());
    
    return E_NONE;
}

