#include "common.h"
#include "stdio.h"
#include "stdlib.h"
#include "gas_monitor.h"
#include "ini.h"
#include "ysp_modbus_slave.h"
#include "windows.h"
#include <vector>
#include "tchar.h" // _sntprintf _T 
#include <Setupapi.h> //SetupDiGetClassDevs Setup*
#include <initguid.h> //GUID
#include <iostream>
#include <string>

#pragma comment (lib, "setupapi.lib")
using namespace std;

//#define CONFIG_FILE_PATH "C:/Users/xydl_lg/Desktop/xying/Ireland/test_ini.ini"
//#define CONFIG_FILE_PATH "C:/Users/xydl_lg/Desktop/xying/Ireland/test_pro/iedModbus_mgr/iedModbus_mgr/yspConfig.ini"
#define CONFIG_FILE_NAME "yspConfig.ini"

#define DELAY_MIN_DEFAULT 10 //程序延迟十分钟执行

#define MIN_SLEEP_TIME(min) min*60*1000  
#define DELAY_RUN(min) MIN_SLEEP_TIME(min) //监测周期


static int pro_delay_min = DELAY_MIN_DEFAULT;

//unsigned char 数组转16进制输出
void xUnsigned_char_hex_out(const char *msg, unsigned char* src, int start, int end)
{
	if (msg != NULL) {
		printf("%s:\n", msg);
	}
	for (int i = start; i < end + 1; i++) {
		printf("%02X", src[i]);
	}

	printf("\n");
}	
void xDelay_run_process()
{
#if 0
    ULONGLONG startTick = 0;
    startTick = GetTickCount64();
    
	printf("delay %d min run ...\n", pro_delay_min);
    Sleep(DELAY_RUN(pro_delay_min));

    _tprintf(_T("sleep : %I64u ms\n"), GetTickCount64() - startTick);
#endif
}

#ifndef GUID_DEVINTERFACE_COMPORT
DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73);
#endif
struct SerialPortInfo
{
    std::string portName;
    std::string description;
};
std::string wstringToString(const std::wstring& wstr)
{

    if (wstr.empty())
    {
        return std::string();
    }
    int size = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
    std::string ret = std::string(size, 0);
    WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &ret[0], size, NULL, NULL); // CP_UTF8
    return ret;
}
bool enumDetailsSerialPorts(vector<SerialPortInfo>& portInfoList)
{
    bool bRet = false;
    SerialPortInfo m_serialPortInfo;
    std::string strFriendlyName;
    std::string strPortName;
    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    // Return only devices that are currently present in a system
    // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID
    // {86E0D1E0-8089-11D0-9CE4-08003E301F73}
    hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (INVALID_HANDLE_VALUE != hDevInfo)
    {
        SP_DEVINFO_DATA devInfoData;
        // The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA)
        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++)
        {
            // get port name
            TCHAR portName[256] = {0};
            HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
            if (INVALID_HANDLE_VALUE != hDevKey)
            {
                DWORD dwCount = 255; // DEV_NAME_MAX_LEN
                RegQueryValueEx(hDevKey, _T("PortName"), NULL, NULL, (BYTE*)portName, &dwCount);
                RegCloseKey(hDevKey);
            }
            // get friendly name
            TCHAR fname[256];
            SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname,
                sizeof(fname), NULL);
#ifdef UNICODE
            strPortName = wstringToString(portName);
            strFriendlyName = wstringToString(fname);
#else
            strPortName = std::string(portName);
            strFriendlyName = std::string(fname);
#endif
            cout << strFriendlyName << endl;
            // remove (COMxx)
            strFriendlyName = strFriendlyName.substr(0, strFriendlyName.find(("(COM")));
            cout << strFriendlyName << endl;
            m_serialPortInfo.portName = strPortName;
            m_serialPortInfo.description = strFriendlyName;
            portInfoList.push_back(m_serialPortInfo);
        }
        if (ERROR_NO_MORE_ITEMS == GetLastError())
        {
            bRet = true; // no more item
        }
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);
    return bRet;
}

//枚举找到使用的串口线对应的端口号
int xEnum_relay_comName(char* com_name)
{
    const char* serial_description = "USB-SERIAL CH340";//项目中使用的串口芯片描述符
    vector<SerialPortInfo> spInfo;
    enumDetailsSerialPorts(spInfo);

    for (int i = 0; i < spInfo.size(); i++)
    {
        std::cout << i << " - " << spInfo[i].portName << " | " << spInfo[i].description << std::endl;
        if (!strncmp(spInfo[i].description.c_str(), serial_description, strlen(serial_description))) {
            memcpy(com_name, spInfo[i].portName.c_str(), spInfo[i].portName.length());
            printf("has find uart number is : %s\n", com_name);
            return true;
        }
    }
  
    return false;
}
int xGet_config_from_configFile()
{
#if 1
    char curr_path[MAX_PATH] = { 0 };
    char configFile_path[MAX_PATH] = { 0 };
    const char* fmt = "%s/%s";

    GetCurrentDirectory(MAX_PATH, curr_path);
    for (int i = 0; i < strlen(curr_path); i++) {
        if(curr_path[i] == 0x5C){ //0x5C \ 反斜杠
            curr_path[i] = 0x2F;  //0x2F / 斜杠
        }
    }
    printf("curr path:%s\n", curr_path);
    snprintf(configFile_path, sizeof(configFile_path), fmt, curr_path, CONFIG_FILE_NAME);
    printf("configFile_path:%s\n", configFile_path);
#endif

    struct ini_t* ini_ctl = ini_load(configFile_path);
    if (ini_ctl == NULL) {
        printf("open config file fail, path:%s\n", configFile_path);
        printf("all parameters use default!!!\n");
        return 1;
    }

    //modbus 从机tcp ipaddr
    const char* ip_addr = ini_get(ini_ctl, "MODBUS", "SLAVE_IP_ADDR");
    xSet_slave_ip_addr(ip_addr);


    //modbus从机tcp port
    const char* tcp_port = ini_get(ini_ctl, "MODBUS", "SLAVE_PORT");
    xSet_slave_port(tcp_port);

    //气体监测的周期(分钟)
    const char* monitor_period = ini_get(ini_ctl, "MONITOR", "GAS_MONITOR_PERIOD");
    xSet_monitor_period(monitor_period);

    const char* uart_buadrate = ini_get(ini_ctl, "MONITOR", "UART_BUADRATE");
    xSet_uart_buadrate(uart_buadrate);

    //气体告警阈值
    const char* H2_threshold = ini_get(ini_ctl, "ALERT", "H2_THRESHOLD");
    xSet_H2_threshold(H2_threshold);
    const char* H2_alert = ini_get(ini_ctl, "ALERT", "H2_alert");
    xSet_H2_alert(H2_alert);

    const char* CO_threshold = ini_get(ini_ctl, "ALERT", "CO_THRESHOLD");
    xSet_CO_threshold(CO_threshold);
    const char* CO_alert = ini_get(ini_ctl, "ALERT", "CO_alert");
    xSet_H2_alert(CO_alert);

    const char* CO2_threshold = ini_get(ini_ctl, "ALERT", "CO2_THRESHOLD");
    xSet_CO2_threshold(CO2_threshold);
    const char* CO2_alert = ini_get(ini_ctl, "ALERT", "CO2_alert");
    xSet_H2_alert(CO2_alert);

    const char* CH4_threshold = ini_get(ini_ctl, "ALERT", "CH4_THRESHOLD");
    xSet_CH4_period(CH4_threshold);

    const char* C2H2_threshold = ini_get(ini_ctl, "ALERT", "C2H2_THRESHOLD");
    xSet_C2H2_threshold(C2H2_threshold);

    const char* C2H4_threshold = ini_get(ini_ctl, "ALERT", "C2H4_THRESHOLD");
    xSet_C2H4_period(C2H4_threshold);

    const char* C2H6_threshold = ini_get(ini_ctl, "ALERT", "C2H6_THRESHOLD");
    xSet_C2H6_threshold(C2H6_threshold);

    //读取程序延迟执行的时间
    const char* process_delay_min = ini_get(ini_ctl, "PROCESS", "DELAY_RUN_MIN");
    if (process_delay_min) {
        pro_delay_min = atoi(process_delay_min);
        printf("delay para %s min\n", process_delay_min);
        printf("delay para: %d min\n", pro_delay_min);
    }
    else {
        pro_delay_min = DELAY_MIN_DEFAULT;
        printf("DELAY_RUN_MIN is null,use default\n");
        printf("delay para: %d min\n", pro_delay_min);
    }

    ini_free(ini_ctl);
    return 0;

}