//
// Created by xajhu on 2021/7/8 0008.
//
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <sds/sds.h>

#include "hardware.h"
#include "user_errno.h"
#include "uthash/uthash.h"
#include "misc.h"
#include "common.h"
#include "config.h"

#define MAX_SENSOR_STR      (32)

//#define CMD_SENSOR_INFO ("ipmitool sensor | awk '{gsub(/\|/, \"\"); print $0}'")
#define CMD_SENSOR_INFO     ("ipmitool sensor")
#define CMD_IPMITOOL_ENABLE ("whereis ipmitool | awk '{print $2}'")

typedef struct {
    char          name[MAX_SENSOR_STR];       ///< 传感器名称
    char          valUnit[MAX_SENSOR_STR];    ///< 传感器值单位
    char          value[MAX_SENSOR_STR];      ///< 传感器值, >=0: 正常值, <0: 错误码
    char          lnr[MAX_SENSOR_STR];        ///< Lower Non-Recoverable
    char          lc[MAX_SENSOR_STR];         ///< Lower Critical
    char          lnc[MAX_SENSOR_STR];        ///< Lower Non-Critical
    char          unc[MAX_SENSOR_STR];        ///< Upper Non-Critical
    char          uc[MAX_SENSOR_STR];         ///< Upper Critical
    char          unr[MAX_SENSOR_STR];        ///< Upper Non-Critical
    char          status[MAX_SENSOR_STR];     ///< 设备状态 0: failed, 1: Ok, and other
    unsigned long timestamp;                  ///< 更新时间戳

    UT_hash_handle hh;
} IPMI_SENSOR_INFO, *PIPMI_SENSOR_INFO;

static const char       *g_ipmiDev[] = {"/dev/ipmi0", "/dev/ipmi/0", "/dev/ipmidev/0"};
static uv_rwlock_t       g_uvLock;
static PIPMI_SENSOR_INFO g_pSensorInfo = NULL;

static int sensor_info_refresh() {
    int   i       = 0;
    int   errCode = ERR_SUCCESS;
    FILE *fp;
    char  buf[1024];
    char *pRet = NULL;

    if (shell_with_output(CMD_IPMITOOL_ENABLE, &pRet) != ERR_SUCCESS || pRet == NULL || strlen(pRet) == 0) {
        if (pRet) {
            free(pRet);
        }
        return -ERR_ITEM_UNEXISTS;
    }

    fp = popen(CMD_SENSOR_INFO, "r");
    if (fp == NULL) {
        return -ERR_OPEN_FILE;
    }

    while (fgets(buf, 1024, fp) != NULL && i < MAX_SENSOR_ITEMS) {
        int  nItems;
        sds  tmpStr = sdsnew(buf);
        sds *pToken = sdssplitlen(tmpStr, (int)sdslen(tmpStr), "|", 1, &nItems);

        if (nItems == 10) {
            PIPMI_SENSOR_INFO pTmp = NULL;

            sdstrim(pToken[0], " \n");
            sdstrim(pToken[1], " \n");
            sdstrim(pToken[2], " \n");
            sdstrim(pToken[3], " \n");
            sdstrim(pToken[4], " \n");
            sdstrim(pToken[5], " \n");
            sdstrim(pToken[6], " \n");
            sdstrim(pToken[7], " \n");
            sdstrim(pToken[8], " \n");
            sdstrim(pToken[9], " \n");

            HASH_FIND_STR(g_pSensorInfo, pToken[0], pTmp);

            if (pTmp) {
                uv_rwlock_wrlock(&g_uvLock);
                strcpy(pTmp->value, pToken[1]);
                strcpy(pTmp->valUnit, pToken[2]);
                strcpy(pTmp->status, pToken[3]);
                pTmp->timestamp = time(NULL);
                uv_rwlock_wrunlock(&g_uvLock);
            } else {
                pTmp = (PIPMI_SENSOR_INFO)malloc(sizeof(IPMI_SENSOR_INFO));

                if (pTmp) {
                    uv_rwlock_wrlock(&g_uvLock);
                    strcpy(pTmp->name, pToken[0]);
                    strcpy(pTmp->value, pToken[1]);
                    strcpy(pTmp->valUnit, pToken[2]);
                    strcpy(pTmp->status, pToken[3]);
                    strcpy(pTmp->lnr, pToken[4]);
                    strcpy(pTmp->lc, pToken[5]);
                    strcpy(pTmp->lnc, pToken[6]);
                    strcpy(pTmp->unc, pToken[7]);
                    strcpy(pTmp->uc, pToken[8]);
                    strcpy(pTmp->unr, pToken[9]);
                    HASH_ADD_STR(g_pSensorInfo, name, pTmp);
                    uv_rwlock_wrunlock(&g_uvLock);
                } else {
                    errCode = -ERR_MALLOC_MEMORY;
                }
            }

            i++;
#if 0
            printf("name: [%s]\n", pTmp->name);
            printf("value: [%s]\n", pTmp->value);
            printf("valUnit: [%s]\n", pTmp->valUnit);
            printf("status: [%s]\n", pTmp->status);
            printf("lnr: [%s]\n", pTmp->lnr);
            printf("lc: [%s]\n", pTmp->lc);
            printf("lnc: [%s]\n", pTmp->lnc);
            printf("unc: [%s]\n", pTmp->unc);
            printf("uc: [%s]\n", pTmp->uc);
            printf("unr: [%s]\n\n\n", pTmp->unr);
#endif
        }

        sdsfreesplitres(pToken, nItems);
        sdsfree(tmpStr);

        memset(buf, 0, sizeof(buf));
    }

    pclose(fp);
    return errCode;
}

int get_sensor_info(PSENSOR_INFO pInfo) {
    PIPMI_SENSOR_INFO pItem, pTmp;
    PSENSOR_ITEM      pSensor;

    if (pInfo == NULL) {
        return -ERR_INPUT_PARAMS;
    }

    if (cfg_get_watch_sensor()) {
        sensor_info_refresh();
    }

    uv_rwlock_rdlock(&g_uvLock);
    pInfo->nItems    = HASH_COUNT(g_pSensorInfo);
    pInfo->timestamp = time(NULL);
    pSensor          = pInfo->sensorInfo;

    HASH_ITER(hh, g_pSensorInfo, pItem, pTmp) {
        pSensor->sensorName = pItem->name;
        pSensor->value      = pItem->value;
        pSensor->unit       = pItem->valUnit;
        pSensor->status     = pItem->status;
        pSensor++;
    }
    uv_rwlock_rdunlock(&g_uvLock);

    return ERR_SUCCESS;
}

int sensor_watch_init() {
    int i, devOk = FALSE;
    int n = ARRAY_SIZE(g_ipmiDev);

    uv_rwlock_init(&g_uvLock);

    for (i = 0; i < n; i++) {
        if (file_exists(g_ipmiDev[i])) {
            devOk = TRUE;
            break;
        }
    }

    if (!devOk) {
        return -ERR_SYS_IPMI_UNSUP;
    }

    if (sensor_info_refresh() != ERR_SUCCESS) {
        return -ERR_SYS_SENSOR_GET_INFO;
    }

    return ERR_SUCCESS;
}