#include "ftu_cfg_parse.h"
#include "md5.h"

#include <iostream>

#include <fstream>


typedef struct 
{
    std::vector<std::string> field_vec;
}stru_csv_row;

typedef struct
{
    std::vector<stru_csv_row> row_vec;
    int capacity;
}stru_csv_data;

typedef struct 
{
    std::string saddr;
    std::string desc;
    std::string type_str;
    std::string default_value;
}stru_param_csv_data;



LOCAL int parse_st_mx_co_dd_csv(const std::string &path, std::vector<stru_st_mx_co_dd_info> &info_vec);


LOCAL stru_ftu_cfg g_ftu_cfg = {};

LOCAL std::vector<stru_param_csv_data> g_param_csv_data_vec = {};

typedef struct
{
    std::string type;
    std::string file_name;
    std::vector<stru_st_mx_co_dd_info> *p_info;
}stru_cfg_file_info;

// LOCAL std::map<std::string, std::vector<stru_st_mx_co_dd_info> *> g_gen_file_list = {
//     {"st.csv", &g_ftu_cfg.st_info_vec},
//     {"mx.csv", &g_ftu_cfg.mx_info_vec},
//     {"co.csv", &g_ftu_cfg.co_info_vec},
//     {"dd.csv", &g_ftu_cfg.dd_info_vec}
// };

LOCAL std::vector<stru_cfg_file_info> g_gen_file_list = {
    {"st", "st.csv", &g_ftu_cfg.st_info_vec},
    {"mx", "mx.csv", &g_ftu_cfg.mx_info_vec},
    {"co", "co.csv", &g_ftu_cfg.co_info_vec},
    {"dd", "dd.csv", &g_ftu_cfg.dd_info_vec}
};



typedef struct
{
    uint8_t type_len;
    uint8_t type_id;
}stru_type_info;

LOCAL std::map<std::string, stru_type_info> g_type_map = 
{
    {"s8",      {1,  DATA_TYPE_S8}},
    {"u8",      {1,  DATA_TYPE_U8}},
    {"s16",     {2,  DATA_TYPE_S16}},
    {"u16",     {2,  DATA_TYPE_U16}},
    {"s32",     {4,  DATA_TYPE_S32}},
    {"u32",     {4,  DATA_TYPE_U32}},
    {"f32",     {4,  DATA_TYPE_F32}},
    {"ip",      {4,  DATA_TYPE_IP}},
    {"mac",     {8,  DATA_TYPE_MAC}},
    {"c8",      {8,  DATA_TYPE_C8}},
    {"c32",     {32, DATA_TYPE_C32}},
    {"c64",     {64, DATA_TYPE_C64}},
    {"c1",      {1,  DATA_TYPE_C1}},
};





stru_ftu_cfg *get_ftu_cfg_ptr(void)
{
    return &g_ftu_cfg;
}





LOCAL std::string trim(const std::string& str) {
    size_t start = str.find_first_not_of(" \t\r\n");
    if (start == std::string::npos) 
        return "";
    size_t end = str.find_last_not_of(" \t\r\n");
    std::string result = str.substr(start, end - start + 1);
    
    // 去除首尾的引号
    if (result.size() >= 2 && result.front() == '"' && result.back() == '"') 
    {
        result = result.substr(1, result.size() - 2);
    }
    
    return result;
}

LOCAL void parse_line(const std::string line, char delimiter, stru_csv_row &row)
{
    std::string field;
    int in_qutoes = 0;

    for(size_t i = 0; i < line.length(); i++)
    {
        char c = line[i];

        if(in_qutoes)
        {
            if(c == '"')
            {
                if(i + 1 < line.length() && line[i+1] == '"')
                {
                    field += c;
                    i++;
                }
                else
                {
                    in_qutoes = 0;
                }
            }
            else
            {
                field += c;
            }
        }
        else
        {
            if(c == delimiter)
            {
                row.field_vec.push_back(trim(field));
                field.clear();
            }
            else if(c == '"')
            {
                in_qutoes = 1;
            }
            else
            {
                field += c;
            }
        }
    }

    row.field_vec.push_back(trim(field));
}

LOCAL stru_csv_data *csv_parse(const std::string csv_file)
{
    std::ifstream fp(csv_file);
    if(!fp.is_open())
    {
        LOG_E("csv_parse: file %s open failed", csv_file.c_str());
        return NULL;
    }

    stru_csv_data *csv_data = new stru_csv_data;
    csv_data->capacity = 0;

    std::string line;
    std::string buffer;
    int line_num = 0;

    while (std::getline(fp, line))
    {
        line_num++;

        if(!line.empty() && line.back() == '\r')
        {
            line.pop_back();
        }

        if(!buffer.empty())
        {
            buffer += "\n" + line;
            line = buffer;
            buffer.clear();
        }
        
        bool in_qutoes = false;
        for(char c : line)
        {
            if(c == '"')
            {
                in_qutoes = !in_qutoes;
            }
        }

        if(in_qutoes)
        {
            buffer = line;
            continue;
        }

        stru_csv_row row;
        parse_line(line, ',', row);

        if(!csv_data->row_vec.empty() && row.field_vec.size() == 1 && csv_data->row_vec.back().field_vec.size()>1)
        {
            if(!csv_data->row_vec.empty())
            {
                csv_data->row_vec.back().field_vec.back() += " " + row.field_vec[0];
            }
            continue;
        }

        csv_data->row_vec.push_back(row);
    }
    
    if(!buffer.empty())
    {
        stru_csv_row row;
        parse_line(buffer, ',', row);
        csv_data->row_vec.push_back(row);
    }

    fp.close();

    return csv_data;
}

LOCAL void csv_data_show(stru_csv_data *p_data)
{
    std::vector<stru_csv_row> &data = p_data->row_vec;
    if (data.empty()) 
    {
        std::cout << "No data" << std::endl;
        return;
    }
    
    // 打印列标题
    std::cout << "Total rows: " << data.size() << std::endl;
    std::cout << "Total columns: " << data[0].field_vec.size() << std::endl << std::endl;
    
    std::cout << "=== Column Headers ===" << std::endl;
    for (size_t j = 0; j < data[0].field_vec.size(); j++) {
        std::cout << "[" << j << "] " << data[0].field_vec[j] << std::endl;
    }

    // 打印前几行作为预览
    int previewRows = data.size();
    for (int i = 1; i < previewRows; i++) {
        std::cout << "Row " << i << " (" << data[i].field_vec.size() << " fields): ";
        for (size_t j = 0; j < data[i].field_vec.size(); j++) 
        { 
            std::string field = data[i].field_vec[j];

            std::cout << "[" << field << "] ";
        }

        std::cout << std::endl;
    }
}

LOCAL void hex_str_to_int(std::string &hex_str, uint16_t &data)
{
    char *endptr;
    data = strtoul(hex_str.c_str(), &endptr, 16);
}


LOCAL int parse_st_mx_co_dd_csv(const std::string &path, const std::string &saddr_prefix, std::vector<stru_st_mx_co_dd_info> &info_vec)
{
    stru_csv_data *p_csv = nullptr;
    if(nullptr == (p_csv = csv_parse(path)))
    {
        LOG_E("parse %s failed", path.c_str());
        return -1;
    }

    for(size_t i = 1; i < p_csv->row_vec.size(); i++)
    {
        stru_csv_row &row = p_csv->row_vec[i];
        if(row.field_vec.size() < 3)
        {
            continue;
        }

        stru_st_mx_co_dd_info info;

        info.saddr = "self_ptl." + saddr_prefix + "." + row.field_vec[2];
        info.desc = row.field_vec[0];
        hex_str_to_int(row.field_vec[1], info.inf);

        info_vec.push_back(info);
    }
    
    delete p_csv;

    return 0;
}

LOCAL void show_st_mx_co_dd_info_vec(const std::vector<stru_st_mx_co_dd_info> &info_vec)
{
    for(const auto &info : info_vec)
    {
        printf("desc: %s, inf: %d, %04x\n", info.desc.c_str(), info.inf, info.inf);
    }
}

LOCAL int parse_param_csv(const std::string &path)
{
    stru_csv_data *p_csv = nullptr;
    if(nullptr == (p_csv = csv_parse(path)))
    {
        LOG_E("parse %s failed", path.c_str());
        return -1;
    }

    for(size_t i = 1; i < p_csv->row_vec.size(); i++)
    {
        stru_csv_row &row = p_csv->row_vec[i];
        if(row.field_vec.size() < 6)
        {
            continue;
        }

        std::string saddr =  row.field_vec[1];
        std::string type_str = row.field_vec[2];
        std::string desc = row.field_vec[3];
        std::string default_value = row.field_vec[5];

        g_param_csv_data_vec.push_back({saddr, desc, type_str, default_value});
    }

    return 0;
}

LOCAL void parse_param_cfg_info(std::string temp, stru_param_cfg &param_cfg)
{
    if(temp.empty())
    {
        return;
    }

    std::string desc = "";
    std::string saddr = "";
    std::string default_value = "";
    std::string type = "";

    size_t pos = temp.find(":");
    if(pos != std::string::npos)
    {
        saddr = temp.substr(0, pos);
        type = temp.substr(pos+1);
    }
    
    if(g_type_map.find(type) == g_type_map.end())
    {
        LOG_E("unknown type: %s", type.c_str());
        return;
    }

    stru_type_info type_info = g_type_map[type];

    std::vector<stru_param_cfg_data> &vec_param = param_cfg.map_type_param[type_info.type_id];

    for(auto &param_csv_data : g_param_csv_data_vec)
    {
        if(param_csv_data.saddr == saddr)
        {
            desc = param_csv_data.desc;
            default_value = param_csv_data.default_value;
            std::string saddr_str = "self_ptl.param." + saddr;
            vec_param.push_back({saddr_str, desc, type, default_value, type_info.type_id, 0});
            break;
        }
    }
}

LOCAL int parse_param_txt(const std::string &path, stru_ftu_cfg &ftu_cfg)
{
    std::ifstream fp(path);
    if(!fp.is_open())
    {
        LOG_E("open %s failed", path.c_str());
        return -1;
    }

    std::string line;
    if(!std::getline(fp, line))
    {
        LOG_E("read %s failed", path.c_str());
        fp.close();
        return -1;
    }
    fp.close();

    // printf("line:len %d, %s\n", line.size(), line.c_str());

    line.append("\n");

    MD5_CTX md5_ctx;
    StartMD5(&md5_ctx);
    EncodeMD5(&md5_ctx, (unsigned char *)line.c_str(), line.size());
    GetMD5(&md5_ctx, ftu_cfg.param_cfg.md5);

    uint8_t temp[64] = {0};
    uint8_t temp_len = 0;
    uint32_t pos = 0;

    for(uint32_t i = 0; i < line.size(); i++)
    {
        if(line[i] != ',' && line[i] != '\n' && line[i] != '\r')
        {
            temp[temp_len++] = line[i];
        }
        else
        {
            temp[temp_len] = '\0';
            parse_param_cfg_info((char*)temp, ftu_cfg.param_cfg);
            temp_len = 0;
        }

        if(i == line.size()-1)
        {
            temp[temp_len] = '\0';
            parse_param_cfg_info((char*)temp, ftu_cfg.param_cfg);
            temp_len = 0;
        }
    }

    for(auto &item : ftu_cfg.param_cfg.map_type_param)
    {
        std::vector<stru_param_cfg_data> &param_vec = item.second;
        uint16_t inf = 0;
        for(auto &param : param_vec)
        {
            param.inf = inf;
            ftu_cfg.param_cfg.vec_param.push_back(param);
            inf++;
        }
    }

    return 0;
}

LOCAL void show_param_cfg(stru_param_cfg &param_cfg)
{
    LOG_I("md5:");
    for(int i = 0; i < 16; i++)
    {
        printf("%02x ", param_cfg.md5[i]);
    }
    printf("\n");

    // for(auto &item : param_cfg.vec_param)
    // {
    //     printf("saddr %s, desc %s, type_str %s, default_value %s, type_id %d, inf %d, %04x\n", 
    //         item.saddr.c_str(), item.desc.c_str(), item.type_str.c_str(), item.default_value.c_str(), item.type, item.inf, item.inf);
    // }
}

int parse_ftu_cfg(const std::string &cfg_path)
{
    for(const auto &cfg : g_gen_file_list)
    {
        std::string path = cfg_path + cfg.file_name;

        if(parse_st_mx_co_dd_csv(path, cfg.type, *cfg.p_info) != 0)
        {
            LOG_E("parse %s failed", path.c_str());
            return -1;
        }
    }

    if(0 != parse_param_csv(cfg_path + "ParamMapping.csv"))
    {
        LOG_E("parse ParamMapping.csv failed");
        return -1;
    }

    if(0 != parse_param_txt(cfg_path + "PARAMCONFIG.txt", g_ftu_cfg))
    {
        LOG_E("parse PARAMCONFIG.txt failed");
        return -1;
    }

    // for(const auto &cfg : g_gen_file_list)
    // {
    //     LOG_I("show %s info:", cfg.first.c_str());
    //     show_st_mx_co_dd_info_vec(*cfg.second);
    // }

    show_param_cfg(g_ftu_cfg.param_cfg);    

    return 0;
}
