/*
 * @Author: zhy
 * @Date: 2022-09-14 09:14:01
 * @LastEditors: qianziyang qianzy@bmser.com
 * @LastEditTime: 2023-12-26 15:48:00
 * @Description: 
 */
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> 
#include <unistd.h> 
#include <netdb.h>  
#include <net/if.h>  
#include <arpa/inet.h> 
#include <sys/ioctl.h>  
#include <sys/types.h>  
#include <sys/time.h> 
#include "bmser_dev.h"
#include "cJSON.h"
#include "bmser_error_code.h"
#include "bmser_project_para.h"

#define MAX_EMG_CNT 16
#define DEV_MAP_CFG_FILE "/app/config/dev_man.json"

typedef struct
{
    bool init;
    uint16_t count;
    BMSER_EMS_MAP_INFO_NODE_S node[MAX_EMG_CNT];
} BMSER_EMS_MAP_INFO_S;

static BMSER_EMS_MAP_INFO_S g_ems_map = {0};

static char g_snvalue[512] = {0};
static int32_t g_snvalue_valid = 0;

/**
 * @description: 获取设备的sn
 * @param {char} *sn
 * @param {uint16_t} sn_size
 * @return {*} 0:success, others:fail.
 */
int bmser_dev_get_board_sn_ex(char *sn, uint16_t sn_size)
{
    FILE *fp = NULL;
    int ret = -1;
    char buf[512] = {0};

    if (sn == NULL || 0 == sn_size) {
        return -1;
    }
    
    if (g_snvalue_valid)
    {
        snprintf(sn, sn_size, "%s", g_snvalue);
        return 0;
    }

    fp = fopen(BOARD_INFO_PATH, "r");
    if (fp == NULL) {
        /* The file could not be opened */
        strcpy(g_snvalue, "21012C00FFFF");
        g_snvalue_valid = 1;
        goto out;
    }

    while (fgets(buf, sizeof(buf), fp) != NULL) {
        if (strstr(buf, "sn = ") != NULL) {
            sscanf(buf, "sn = %" STR(SN_MAX_LEN) "[a-zA-Z0-9]", sn);
            ret = 0;
            break;
        }
    }

    memmove(g_snvalue, sn, strlen(buf) < sn_size ? strlen(buf) : sn_size - 1);
    g_snvalue_valid = 1;
out:
    if (fp != NULL) {
        fclose(fp);
    }
    return ret;
}


/**
 * @description: 获取dev_sn,一个8位设备地址(十进制),代表意义为deviceID
 * @param {const struct mosquitto_message} *message
 * @return {int} dev_sn
 */
int bmser_dev_get_dev_sn(char* topic)
{
    if (topic == NULL || strlen(topic) == 0) {
        // 处理空指针或者非法字符串的情况
        return -1;
    }
    
    int ret = 0 , dev_sn = 0;
    ret = sscanf(topic, "%*[^/]/%*[^/]/%*[^/]/%d", &dev_sn);
    if (1 != ret)
    {
        return -1;//解析失败
    }
    return dev_sn;    
}

/**
 * @description: 获取设备sn号，设备sn号 = sn + 设备id
 * @param {char} *dev_sn
 * @param {int} len
 * @param {int} devid
 * @return {*}
 */
int bmser_dev_get_board_dev_sn(char *dev_sn, int len, int devid)
{
    if (NULL == dev_sn || len <= 0 || devid < 0) {
        return -1;
    }
    int ret = 0;
    ret = bmser_dev_get_board_sn(dev_sn);
    if (0 != ret) {
        return ret;
    }
    
    snprintf(dev_sn + strlen(dev_sn), len - strlen(dev_sn), "%02d", devid);
    return 0;
}

// 获取本机ip  eth_inf网卡名称 调用方法get_local_ip("apcli0", ip);
int bmser_dev_get_local_ip(const char *eth_inf, char *ip)
{
    if (NULL == eth_inf || NULL == ip) {
        return -1;
    }

    int sd;
    struct ifreq ifr;
    char emsg[128] = {0};
 
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sd) {
        strerror_r(errno, emsg, sizeof(emsg));
        dy_syslog(LOG_ERR, "socket error: %s.", emsg);
        return -1;
    }
 
    strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
 
    // if error: No such device  
    if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) {
        strerror_r(errno, emsg, sizeof(emsg));
        dy_syslog(LOG_ERR, "ioctl error: %s", emsg);
        close(sd);
        return -1;
    }
    
    dy_syslog(LOG_DEBUG, "interfac: %s, ip: %s.", eth_inf, inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr)); 
    strcpy(ip, inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
    close(sd);
    return 0;
}

int bmser_dev_get_network_ip(const char *eth_inf, char *netwrok_addr, int size)
{
    if (eth_inf == NULL || netwrok_addr == NULL) {
        return -1;
    }

    struct ifreq ifr;
    int sockfd = 0, len = 0, i = 0;
    char ip[16] = {0};
    char netmask[16] = {0};

    int sock_netmask;   
    struct ifreq ifr_mask;  
    struct sockaddr_in *net_mask;  
    int ip_addr[4], mask_addr[4];

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    strcpy(ifr.ifr_name, eth_inf);

    if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) {
        perror("ioctl error");
        close(sockfd);
        return -1;
    }
 
    strcpy(ip, inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
    dy_syslog(LOG_DEBUG, "get cur ip %s.", ip);
    close(sockfd);
          
    sock_netmask = socket(AF_INET, SOCK_STREAM, 0);  
    if (sock_netmask == -1) {  
        dy_syslog(LOG_ERR, "create socket failture...");  
        return -1;  
    }  
      
    memset(&ifr_mask, 0, sizeof(ifr_mask));     
    strncpy(ifr_mask.ifr_name, eth_inf, sizeof(ifr_mask.ifr_name )-1);     
  
    if ((ioctl( sock_netmask, SIOCGIFNETMASK, &ifr_mask)) < 0) {  
        dy_syslog(LOG_ERR, "mac ioctl error");
        close(sock_netmask); 
        return -1;  
    }  
      
    net_mask = (struct sockaddr_in * )&(ifr_mask.ifr_netmask);  
    strcpy(netmask, inet_ntoa(net_mask->sin_addr));             
    close(sock_netmask); 
    dy_syslog(LOG_DEBUG, "get cur dev mask %s.", netmask);

    //获取网络id
    sscanf(ip, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3]);
    sscanf(netmask, "%d.%d.%d.%d", &mask_addr[0], &mask_addr[1], &mask_addr[2], &mask_addr[3]);

    for (i = 0; i < sizeof(ip_addr)/sizeof(ip_addr[0]); i++) {
        len += snprintf(netwrok_addr + len, size - len, "%d.", ip_addr[i] & mask_addr[i]);
    }

    netwrok_addr[len - 1] = '\0';
    dy_syslog(LOG_DEBUG, "get network id %s.", netwrok_addr);

    return 0;
}

static int32_t parse_ems_map_file(const char* file)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(file, OPRT_PARA_NULL);

    char *p = NULL;
    FILE *fd = NULL;
    int len = 0, read_len = 0;
    uint16_t index = 0;
    BMSER_EMS_MAP_INFO_NODE_S ems_map[MAX_EMG_CNT] = {0};

    cJSON *root = NULL, *ems = NULL, *item = NULL, *tmp_key = NULL;

    fd = fopen(file, "r");
    BMSER_CHECK_PARA_NULL_RETURN_VAL(fd, OPRT_OPEN_FILE_FAIL);

    fseek(fd, 0, SEEK_END);
    len = ftell(fd);
    if (len <= 0) {
        goto ERR_EXIT;
    }
    
    fseek(fd, 0, SEEK_SET);

    p = (char *)malloc(len + 1);
    BMSER_CHECK_PARA_NULL_GOTO(p, ERR_EXIT);

    memset(p, 0, len + 1);
    read_len = fread(p, 1, len, fd);
    if (read_len != len) {
        goto ERR_EXIT;
    }
    fclose(fd);
    fd = NULL;

    root = cJSON_Parse((const char *)p);
    BMSER_CHECK_PARA_NULL_GOTO(root, ERR_EXIT);
    free(p);
    p = NULL;

    ems = cJSON_GetObjectItem(root, "emsDeploy");
    BMSER_CHECK_PARA_NULL_GOTO(ems, ERR_EXIT);

    item = ems->child;
    while (item != NULL) {
        tmp_key = cJSON_GetObjectItem(item, "devSn");
        if (!tmp_key || !cJSON_IsString(tmp_key) || !cJSON_GetStringValue(tmp_key)) {
            goto ERR_EXIT;
        }
        snprintf(ems_map[index].devSn, sizeof(ems_map[index].devSn), "%s", cJSON_GetStringValue(tmp_key));

        tmp_key = cJSON_GetObjectItem(item, "ip");
        if (!tmp_key || !cJSON_IsString(tmp_key) || !cJSON_GetStringValue(tmp_key)) {
            goto ERR_EXIT;
        }
        snprintf(ems_map[index].ip, sizeof(ems_map[index].ip), "%s", cJSON_GetStringValue(tmp_key));

        tmp_key = cJSON_GetObjectItem(item, "isHost");
        if (!tmp_key || !cJSON_IsNumber(tmp_key)) {
            goto ERR_EXIT;
        }
        ems_map[index].isHost = (int)cJSON_GetNumberValue(tmp_key);

        tmp_key = cJSON_GetObjectItem(item, "serialId");
        if (!tmp_key || !cJSON_IsNumber(tmp_key)) {
            goto ERR_EXIT;
        }
        ems_map[index].serialId = (int)cJSON_GetNumberValue(tmp_key);

        dy_syslog(LOG_DEBUG, "devSn = %s, ip = %s, ishost = %d, serialId = %u.", ems_map[index].devSn, \
        ems_map[index].ip, ems_map[index].isHost, ems_map[index].serialId);
        
        if (index >= MAX_EMG_CNT - 1) {
            break;
        } else {
            index++;
        }
        item = item->next;
    }

    g_ems_map.count = index;
    memcpy(g_ems_map.node, ems_map, sizeof(g_ems_map.node));
    g_ems_map.init = true;

    cJSON_Delete(root);
    return OPRT_OK;

ERR_EXIT:
    if (root) {
        cJSON_Delete(root);
        root = NULL;
    }    
    if (p) {
        free(p);
        p = NULL;
    }
    if (fd) {
        fclose(fd);
        fd = NULL;
    }
    return OPRT_COMM_ERROR;
}

/**
 * @brief       获取配置的EMG数量
 * @param[in]
 * @return      int32_t
 * @retval      EMG数量
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-10-26           zhy        创建初始版本          \n
*/
int32_t bmser_dev_get_ems_emg_conut()
{
    if (false == g_ems_map.init) {
        if (0 != parse_ems_map_file(DEV_MAP_CFG_FILE)) {
            return OPRT_NOT_INIT;
        }
    }

    return g_ems_map.count;
}


/**
 * @brief       获取EMG单个节点信息
 * @param[out]  BMSER_EMS_MAP_INFO_NODE_S* emsMap 
 * @param[in]   int32_t index EMG设备下标
 * @return      int32_t
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-10-26           zhy        创建初始版本          \n
*/
int32_t bmser_dev_get_ems_map_config(BMSER_EMS_MAP_INFO_NODE_S* emsMap, int32_t index)
{
    if (false == g_ems_map.init) {
        if (0 != parse_ems_map_file(DEV_MAP_CFG_FILE)) {
            return OPRT_NOT_INIT;
        }
    }

    if (!emsMap || index < 0 || g_ems_map.count <= index) {
        return OPRT_PARA_NULL;
    }

    memcpy(emsMap, &g_ems_map.node[index], sizeof(BMSER_EMS_MAP_INFO_NODE_S));

    return OPRT_OK;
}


/**
 * @brief       判断设备是否主柜
 * @param[in]   void
 * @return      bool
 * @retval      true:主柜  false:从柜
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-11-02           JML        创建初始版本          \n
*/
bool bmser_dev_is_host_cabinet(void)
{
    char host_sn[64] = {0};
    char dev_sn[64] = {0};
    int i = 0;
    bool ret = false;
    
    for (i = 0; i < bmser_dev_get_ems_emg_conut(); i++) {
        if (g_ems_map.node[i].isHost) {
            memcpy(host_sn, g_ems_map.node[i].devSn, sizeof(g_ems_map.node[i].devSn));
            break;
        }
    }

    bmser_dev_get_board_sn_ex(dev_sn, sizeof(dev_sn));
    if (strncmp(dev_sn, host_sn, sizeof(dev_sn)) == 0) {
        ret = true;
    }
    return ret;
}

/**
 * @brief       获取主柜设备ip
 * @param[in]   char *ipAddr
 * @param[in]   uint16_t len
 * @return      int32_t
 * @retval      0:成功  other:失败
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-11-09           ZHY        创建初始版本          \n
*/
int32_t bmser_dev_get_host_cabinet_ip(char *ipAddr, uint16_t len)
{
    if (NULL == ipAddr || len == 0) {
        return OPRT_PARA_NULL;
    }
    
    int32_t i = 0;
    
    for (i = 0; i < bmser_dev_get_ems_emg_conut(); i++) {
        if (g_ems_map.node[i].isHost) {
            snprintf(ipAddr, len, "%s", g_ems_map.node[i].ip);
            return OPRT_OK;
        }
    }

    return OPRT_NOT_FOUND;
}

/**
 * @brief       根据topic中devsn截取bankId以及rackId
 * @param[in]   char *topic
 * @param[out]  uint16_t *bankId
 * @param[out]  uint16_t *rackId
 * @return      int32_t
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-19           JHC        创建初始版本          \n
*/
int32_t bmser_dev_get_bankid_from_topic(char *topic, uint16_t *bankId, uint16_t *rackId)
{
    if (!topic || 0 == strlen(topic) || !bankId || !rackId) {
        return OPRT_PARA_NULL;
    }

    int32_t ret = 0, devSn = 0;
    uint16_t bank_id = 0, rack_id = 0;
    ret = sscanf(topic, "%*[^/]/%*[^/]/%*[^/]/%08d", &devSn);
    if (1 != ret) {
        return OPRT_DATA_INVALID;
    }

    rack_id = devSn % 1000;
    bank_id = (devSn / 1000 % 100);
    *bankId = bank_id;
    *rackId = rack_id;

    return OPRT_OK;
}

/**
 * @brief       根据bankid与rackid计算实际设备的设备ID
 * @param[in]    uint16_t bankId
 * @param[in]    uint16_t rackId
 * @param[out]   uint16_t *actualDevAddr
 * @return      int32_t
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-19           JHC        创建初始版本          \n
 * 2023-12-19           JHC        通过单堆下簇数接口计算实际物理设备ID   \n
*/
int32_t bmser_dev_calcActualDeviceAddress(uint16_t bankId, uint16_t rackId, uint16_t *actualDevAddr)
{
    if(actualDevAddr == NULL) { return -1; }

    uint16_t singleBankRacks = bmser_jsonGetSingleBankRackNum();
    *actualDevAddr = bankId * singleBankRacks + rackId;
    return 0;
}

/**
 * @brief        根据设备实际ID计算堆id和簇id
 * @param[in]   uint16_t devId
 * @param[out]  uint16_t *bankId
 * @param[out]  uint16_t *rackId
 * @return      int32_t
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-21           ZHY        创建初始版本          \n
 * 2023-12-26           QZY        添加对动环(第62簇)的特殊处理  \n
*/
int32_t bmser_dev_calcBankIdAndRackIdByDevId(uint16_t devId, uint16_t *bankId, uint16_t *rackId)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(bankId, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(rackId, OPRT_PARA_NULL);

    int32_t singleBankRacks = bmser_jsonGetSingleBankRackNum();
    //devId为0表示为堆，bankId与rackId都应为0，rackId有效地址从1开始
    if (0 == devId) {
       *bankId = 0;
       *rackId = 0;
    } else if(62 == devId) {
        //devId为62表示为动环信息，rackId固定为62，bankId单柜时为0，多柜时为实际柜编号
        *bankId = 0;
        *rackId = devId;
    } else {
        *bankId = (devId - 1) / singleBankRacks;
        *rackId = devId - (singleBankRacks*(*bankId));
    }

    return 0;
}