#include "thread_func.hpp"
#include <map>
#include <time.h>
#include <math.h>
#include <string.h>
#include <cstdlib>
#include <linux/types.h>
#include <sys/sysinfo.h>
#include <dirent.h>
#include <boost/typeof/typeof.hpp>
#include <boost/filesystem.hpp>
#include <zlog.h>
#include <json/json.h>
#include "mqttclient/mqtt_client.h"
#include "uart/uart.hpp"
#include "common/common_func.hpp"
#include "wifi_5g/dial.h"
#include "wifi_5g/wpa_client.h"
#include "utility/tcp_cgi.hpp"
#include "utility/udp_scan.hpp"
#include "utility/search_dev.hpp"
#include "localserver/local_server.hpp"
#include "localserver/cmt_server.hpp"

extern zlog_category_t *zct;
extern zlog_category_t *zbt;
static std::string serverPort;

Dial dial;
static int clean_memory = 0;
std::unique_ptr<CMTServer> g_mgr_server;

void StartCgiServer() {
    zlog_info(zbt, "start deal cgi");

    while (1) {
        TcpCgi::startCgiServer();
        sleep(10);
    }
}

void RunLED() {
    while (1) {
        gpio_set(GlobalConfig::GPIO_G.runLed, 1);
        sleep(1);
        gpio_set(GlobalConfig::GPIO_G.runLed, 0);
        sleep(1);
    }
}

void StartCMTServer() {
	sleep(2);
    boost::asio::io_service io_service;   
    g_mgr_server = std::make_unique<CMTServer>(io_service, 10000);
    io_service.run();
}

void HeartRep() {
    int count  = 0;
    while (1) {
        Json::Value jsHeart;
        Json::FastWriter fw;
        jsHeart["dataNodeGatewayNo"] = GlobalConfig::MacAddr_G;
        jsHeart["status"] = "online_V" + GlobalConfig::Version;
        jsHeart["IP"] = GlobalConfig::IpAddr_G;
        std::string strJson = fw.write(jsHeart);
        int iRet = data_publish(strJson.c_str(), GlobalConfig::Topic_G.mPubHeart.c_str());
        if (iRet != 0) {
            zlog_warn(zct, "fail to send heart mqtt msg");           
        }
        if (count == 10){
            zlog_warn(zct, "heart = %s,iRet = %d", strJson.c_str(), iRet);
            count = 0;
        }else{
            zlog_info(zct, "heart = %s,iRet = %d", strJson.c_str(), iRet);
        }
        
        if (iRet != 0) {
            gpio_set(GlobalConfig::GPIO_G.errorLed, 1);
            sleep(1);
            gpio_set(GlobalConfig::GPIO_G.errorLed, 0);
            sleep(1);
            gpio_set(GlobalConfig::GPIO_G.errorLed, 1);
            sleep(1);
            gpio_set(GlobalConfig::GPIO_G.errorLed, 0);
            sleep(1);
            gpio_set(GlobalConfig::GPIO_G.errorLed, 1);
            sleep(1);
            GlobalConfig::serverStatus = 1;
        } else {
            GlobalConfig::serverStatus = 0;
            gpio_set(GlobalConfig::GPIO_G.errorLed, 0);
        }
        sleep(30);
        count ++;
    }
}

void GetCSQ() {
#ifdef NR5G_MODULE
    // 5G
    int iRet = -1;
open5G:
    iRet = dial.openPort("/dev/ttyUSB2");
    if (iRet < 0) {
        sleep(5);
        goto open5G;
    }
#ifdef NR5G_MEIGE
    dial.closePort();
    char szquectel[100] = {0x00};
    std::string strAPN = ReadStrByOpt(SERVERCONFIG, "Server", "APN");
    sprintf(szquectel, "/opt/quectel-CM/Meig-CM -s %s &", strAPN.c_str());
    system(szquectel);
#else
    dial.recvData();
#endif
#endif
#ifdef Q4G_MODULE
    // 4G
    int fd = -1;
open4G:
    fd = config_uart("/dev/ttyUSB2", 115200);
    if (fd < 0) {
        sleep(5);
        goto open4G;
    }
    char szbuffer[200] = {0x00};
    int offSize = 0;
    int timeoutflag = 0;
    write_data(fd, (char*)"AT+QENG=\"servingcell\"\r\n", 27);
    while (1) {
        char buff[1024] = {0x00};
        int ret = read_data(fd, buff, 1024, 10);
        if (ret <= 0) {
            timeoutflag++;
            if (timeoutflag > 5) {
                zlog_info(zct,"timeoutflag = %d\n", timeoutflag);
                timeoutflag = 0;
                const char *str2 = "+QENG: ";
                char csq[128] = {0};
                char *pdata = strstr((char *)szbuffer, str2);
                if (pdata) {
                    strncpy(csq, pdata + 7, sizeof(csq));

                    GlobalConfig::NetStatus = GetOneContent(csq, 1, ",");
                    std::string signal = GetOneContent(csq, 13, ",");
                    GlobalConfig::NetSignal = atoi(signal.c_str());
                    GlobalConfig::NetType = GetOneContent(csq, 2, ",");
                    zlog_info(zct, "NetStatus = %s,NetSignal = %d", GlobalConfig::NetStatus.c_str(), GlobalConfig::NetSignal);
                }
                memset(szbuffer, 0x00, sizeof(szbuffer));
                offSize = 0;
                write_data(fd, (char*)"AT+QENG=\"servingcell\"\r\n", 27);
            }
            usleep(10000);
        } else if (ret > 0) {
            zlog_info(zct, "ret = %d,buff = %s", ret, buff);
            memcpy(szbuffer + offSize, buff, ret);
            offSize = offSize + ret;
            zlog_info(zct, "szbuffer = %s", szbuffer);
            continue;
        }
        sleep(10);
    }
#endif
}

void Dial5G() {
    sleep(2);
dial5G:
    if (dial.m_fd > 0) {
        dial.dial5G();
    } else {
        sleep(5);
        goto dial5G;
    }
}

void UartStart() {
    zlog_info(zbt, "zigAckrep = %d,zigAckreset = %d,zigReset = %d,errorLed = %d,power = %d", GlobalConfig::GPIO_G.zigAckrep, GlobalConfig::GPIO_G.zigAckreset, GlobalConfig::GPIO_G.zigReset, GlobalConfig::GPIO_G.errorLed, GlobalConfig::GPIO_G.power);
    InitGpio(GlobalConfig::GPIO_G.zigAckrep, 0);   // ACK
    InitGpio(GlobalConfig::GPIO_G.zigAckreset, 1); // ACK	reset
    InitGpio(GlobalConfig::GPIO_G.zigReset, 1);    // Zigbee reset
    gpio_set(GlobalConfig::GPIO_G.zigAckreset, 1);
    gpio_set(GlobalConfig::GPIO_G.zigReset, 1);
    zlog_info(zbt, "GPIO Init");

    InitGpio(GlobalConfig::GPIO_G.errorLed, 1); //指示灯
    gpio_set(GlobalConfig::GPIO_G.errorLed, 0);
    InitGpio(GlobalConfig::GPIO_G.power, 1); // power reset
    gpio_set(GlobalConfig::GPIO_G.power, 0);

    GlobalConfig::Zigbee_G.Serial_Rate = 0x07;
    GlobalConfig::Zigbee_G.Serial_DataB = 0x08;
    GlobalConfig::Zigbee_G.Serial_StopB = 0x01;
    uart_inst::instance().InitUart(B115200);
    char buffer[1100] = {0x00};

    sleep(1);
    uart_inst::instance().UartRecv(uart_inst::instance().fd, 1, buffer);
    sleep(1);
}

void InitModule() {
#ifdef NR5G_MODULE
    InitGpio(GlobalConfig::GPIO_G.commRest, 1);
    gpio_set(GlobalConfig::GPIO_G.commRest, 1);
#endif // NR5G_MODULE

#ifdef Q4G_MODULE
#endif // Q4G_MODULE
#ifdef WIFI_MODULE
    zlog_info(zbt, "Init WiFi!");
    InitGpio(GlobalConfig::GPIO_G.wifiReset, 1); // WiFi模组复位，0复位，1取消复位
    gpio_set(GlobalConfig::GPIO_G.wifiReset, 1);
    sleep(10);
    wifi::WPAClient wpa;
    wpa.ReconnectWiFi();
    system("/etc/init.d/wpa_restart");
    system("udhcpc -i wlan2 &");
#endif // WIFI_MODULE
}

void TestUart() {
    uart_inst::instance().InitTestUart(B115200);
    uart_inst::instance().ReadTestUart();
}

void UartStartWave() { uart_inst::instance().DealWaveThread(); }

void StartUdpSys() { udp_sys::instance().StartConnectSysUdp(); }

void WatchDog() {
    int fd = OpenWatchDog();
    while (1) {
        WriteWatchDog(fd);
        sleep(50);
    }
    close(fd);
}

void StartMqttClient() {
    zlog_info(zct, "start mqtt \n");
    std::string runinfo = "MQTT通信模块启动";

    while (1) {
        if (GlobalConfig::ServerIP.length() > 0) {
            std::string strEqupNo = GlobalConfig::MacAddr_G;
            std::string strVersion = GlobalConfig::Version;
            std::string salt;            
            start_client(strEqupNo.c_str(), GlobalConfig::MacAddr_G.c_str(), GlobalConfig::ServerIP.c_str(), strVersion.c_str(), "11111111", salt);
            zlog_error(zct, "fail to connect server");
        }
        sleep(10);
    }
}

void SearchThread() {
    std::string runinfo = "设备搜索模块启动";

    while (GlobalConfig::QuitFlag_G) {
        if (GlobalConfig::IpAddr_G.length() > 0 && 0 != GlobalConfig::IpAddr_G.compare("0.0.0.0")) {
            zlog_info(zct, "%s\n", runinfo.c_str());
            boost::asio::io_service io_service;
            SearchDev *searchDevObj = new SearchDev(io_service);
            searchDevObj->MultiCastRecv();
            io_service.run();
            delete searchDevObj;
        }
        sleep(5);
        zlog_info(zct, "SearchThread restart.");
    }
}

void RecvUpdateFile() {
    boost::asio::io_service iosev;
    boost::asio::ip::tcp::acceptor acceptor(iosev, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 7304));
    for (;;) {
        boost::asio::ip::tcp::socket socket(iosev);
        acceptor.accept(socket);
        boost::system::error_code ec;
        if (ec) {
            zlog_error(zct, "%s\n", boost::system::system_error(ec).what());
        }
        FILE *fp;
        char buffer[1024];
        size_t len = 0;
        size_t write_len;
        bzero(buffer, 1024);
        fp = fopen("/tmp/upgrade.tar.gz", "w");
        if (NULL == fp) {
            zlog_error(zbt, "File:/tmp/upgrade.tar.gz Can Not Open");
            return;
        }

        while ((len = socket.read_some(boost::asio::buffer(buffer), ec))) {
            write_len = fwrite(buffer, sizeof(char), len, fp);
            if (write_len < len) {
                zlog_info(zct, "File:test Write Failed!");
                break;
            }
            bzero(buffer, 1024);
        }
        zlog_info(zbt, "Receive File From Server Finished!");
        fclose(fp);
        Json::Value jsData;
        Json::FastWriter fw;
        jsData["cmd"] = "03";
        jsData["updatefilename"] = "updatefile";
        std::string str = fw.write(jsData);
        zlog_warn(zbt, "==============upgrade start===============");
        system("/etc/init.d/sysupgrade.sh");        
    }
}

bool InsufficientFreeMemory() {
    long mem_free = -1;
    char name1[20];
    std::string strMemFree = GetFileContent("/proc/meminfo", 2);
    sscanf(strMemFree.c_str(), "%s%ld", name1, &mem_free);
    mem_free = mem_free / 1024;    
    if (mem_free < 80) {
        zlog_warn(zct, "free memory: %ld MB", mem_free);
        return true;
    } else {
        return false;
    }
}

void CleanLogFile(std::string log_dir, size_t retain) {
    std::vector<LogFileProperty> vec;
    boost::filesystem::path directory = log_dir;
    try {
        if (boost::filesystem::exists(directory) && boost::filesystem::is_directory(directory)) {
            for (auto&& file : boost::filesystem::directory_iterator(directory)) {
                if (boost::filesystem::is_regular_file(file)) {
                    LogFileProperty file_prop;
                    std::cout<<"["<<file.path()<<"]"<<std::endl;
                    file_prop.name = file.path().string();
                    file_prop.time_stamp = boost::filesystem::last_write_time(file);
                    vec.push_back(file_prop);
                }
            }
            if (vec.size() > 0) {
                std::sort(vec.begin(), vec.end());
                if (vec.size() <= retain) {
                    zlog_warn(zct, "[DiskSpaceCheck] there is no enough file to clean:%d", vec.size());
                    return;                    
                }
                int total = vec.size();                              
                
                for (int i = retain; i < total; ++i) {
                    boost::filesystem::remove(vec[i].name);
                    zlog_warn(zct, "[DiskSpaceCheck] *** delete %s", vec[i].name.c_str());                                              
                }
            }
        }
    } catch (const boost::filesystem::filesystem_error& ex) {
        zlog_error(zct, " Error accessing directory: %s", ex.what());
    } 
}

void DiskSpaceCheck() {
    std::string directory = "/opt/log";
    std::string directory_data = "/opt/data";
    while (1) {
        boost::filesystem::space_info si = boost::filesystem::space("/opt");                
        if (si.free <= 20000000) {     
            zlog_warn(zct, "disk free space:%llu for /opt", si.free);       
            CleanLogFile(directory, 10);   
            CleanLogFile(directory_data, 100);           
            boost::filesystem::space_info si1 = boost::filesystem::space("/opt");
            if (si1.free <= 20000000) {
                zlog_warn(zct, "[DiskSpaceCheck] can not release enough space and clean more log file");
                CleanLogFile(directory, 5);
            }          
        }
        
        if (clean_memory == 10) { // 10*30 = 300秒清理一次内存
            clean_memory = 0;
            if (InsufficientFreeMemory()) {
                system("sync");
                sleep(10);
                system("echo 3 > /proc/sys/vm/drop_caches"); 
                if (InsufficientFreeMemory()) {
                    zlog_error(zct, "can not release more space, so reboot");
                    system("sync");
                    sleep(3);
                    system("reboot");
                }    
            }
        } else {
            clean_memory++;
        }
        sleep(30);
    }
}
