#include "income.h"
#include "../common.h"
#include "deviceLayer.h"
#include "define.h"
#include <string.h>
#include <time.h>
#include <math.h>
/*
{
    "price": [
        {
            "month":[1,2,3],
            "top": 2.7,
            "peak": 34.6,
            "flat": 6.78,
            "valley": 67.89
        },
        {
            "month":[4,5,6],
            "top": 12.7,
            "peak": 4.6,
            "flat": 56.78,
            "valley": 67.89
        },
        {
            "month":[7,8,9,10,11,12],
            "top": 1.7,
            "peak": 34.6,
            "flat": 56.78,
            "valley": 6.89
        }
    ]
}
*/
#if 0
static int ele_price_init_by_json_str(income_host *h, const char *json_str)
{
    int ret = 0;
    ele_price price_tmp;
    struct json_to_struct_map
    {
        const char *json_key;
        double *p_value;
    } tmp_map[] = {
        {.json_key = "top", .p_value = &price_tmp.top},
        {.json_key = "peak", .p_value = &price_tmp.peak},
        {.json_key = "flat", .p_value = &price_tmp.flat},
        {.json_key = "valley", .p_value = &price_tmp.valley},
    };

    cJSON *root = NULL;
    root = json_parse_string_with_comments(json_str);
    if (!root)
    {
        ems_syslog(LOG_ERR, "parse cfg file error");
        return -1;
    }

    cJSON *price_obj = cJSON_GetObjectItemCaseSensitive(root, "price");
    if (price_obj == NULL)
    {
        ems_syslog(LOG_ERR, "get obj price error");
        ret = -2;
        goto ele_price_init_by_json_str_exit;
    }

    cJSON *tmp = NULL;
    cJSON *tmp_obj = NULL;
    int price_sum = cJSON_GetArraySize(price_obj);

    for (int j = 0; j < price_sum; j++)
    {
        tmp_obj = cJSON_GetArrayItem(price_obj, j);
        for (int i = 0; i < sizeof(tmp_map) / sizeof(tmp_map[0]); i++)
        {
            tmp = cJSON_GetObjectItemCaseSensitive(tmp_obj, tmp_map[i].json_key);
            if (tmp == NULL)
            {
                ems_syslog(LOG_ERR, "get obj %s error", tmp_map[i].json_key);
                ret = -3 - i;
                goto ele_price_init_by_json_str_exit;
            }
            *tmp_map[i].p_value = tmp->valuedouble;
        }

        tmp_obj = cJSON_GetObjectItemCaseSensitive(tmp_obj, "month");
        if (tmp_obj == NULL)
        {
            ems_syslog(LOG_WARNING, "get obj month error");
        }

        ret = cJSON_GetArraySize(tmp_obj);
        for (int n = 0; n < ret; n++)
        {
            tmp = cJSON_GetArrayItem(tmp_obj, n);
            int m = tmp->valueint - 1;
            if ((m >= 0) && (m < sizeof(h->price) / sizeof(h->price[0])))
            {
                h->price[m].price = price_tmp;
                h->price[m].valid = 1;
            }
            else
            {
                ems_syslog(LOG_WARNING, "month cfg error at %d", n);
            }
        }
    }

    ems_syslog(LOG_NOTICE, "electricity price:");
    for (int i = 0; i < sizeof(h->price) / sizeof(h->price[0]); i++)
    {
        if (h->price[i].valid != 0)
        {
            ems_syslog(LOG_NOTICE, "month %2d price: top=%f, peak:%f, flat:%f, valley=%f", i + 1, h->price[i].price.top, h->price[i].price.peak, h->price[i].price.flat, h->price[i].price.valley);
        }
        else
        {
            ems_syslog(LOG_WARNING, "month %d price: invalid", i);
        }
    }

ele_price_init_by_json_str_exit:
    cJSON_Delete(root);
    return ret;
}

static void energy_sub(const ele_energy *minuend, const ele_energy *subtrahend, ele_energy *diff)
{
    diff->top = minuend->top - subtrahend->top;
    diff->peak = minuend->peak - subtrahend->peak;
    diff->flat = minuend->flat - subtrahend->flat;
    diff->valley = minuend->valley - subtrahend->valley;
    diff->total = minuend->total - subtrahend->total;
}

static int get_energy(income_meter *meter, ele_energy *act, ele_energy *react)
{
    int online = dev_get_dev_tag_int(meter->dev_no, "Online");
    if (online == 0)
    {
        return -1;
    }
    act->top = dev_get_dev_tag_float(meter->dev_no, meter->act_top_tag);
    act->peak = dev_get_dev_tag_float(meter->dev_no, meter->act_peak_tag);
    act->flat = dev_get_dev_tag_float(meter->dev_no, meter->act_flat_tag);
    act->valley = dev_get_dev_tag_float(meter->dev_no, meter->act_valley_tag);
    act->total = dev_get_dev_tag_float(meter->dev_no, meter->act_total_tag);

    react->top = dev_get_dev_tag_float(meter->dev_no, meter->rev_top_tag);
    react->peak = dev_get_dev_tag_float(meter->dev_no, meter->rev_peak_tag);
    react->flat = dev_get_dev_tag_float(meter->dev_no, meter->rev_flat_tag);
    react->valley = dev_get_dev_tag_float(meter->dev_no, meter->rev_valley_tag);
    react->total = dev_get_dev_tag_float(meter->dev_no, meter->rev_total_tag);

    return 0;
}

int update_today_start_energy(income_host *h)
{
    energy *today_energy = &h->today_energy;
    time_t t = time(NULL);
    struct tm *local = localtime(&t);
    int cur_date = ((local->tm_year + 1900) * 10000) + ((local->tm_mon + 1) * 100) + local->tm_mday;

    const char *cfg_f = "/app/config/today_start_energy.json";
    char *f_data = read_file_data(cfg_f);
    if (f_data == NULL)
    {
        ems_syslog(LOG_ERR, "load sys config file error, file:%s\n", cfg_f);
        f_data = strdup("{\"time\":0}");
    }

    cJSON *root = json_parse_string_with_comments(f_data);
    cJSON *tmp_obj = NULL;
    struct
    {
        char *name;
        ele_energy *energy;
    } p_map[] = {
        {.name = "actEnergy", &today_energy->active_energy},
        {.name = "reaEnergy", &today_energy->reactive_energy},
    };
    if (root != NULL)
    {
        tmp_obj = cJSON_GetObjectItemCaseSensitive(root, "time");
        if (tmp_obj->valueint == cur_date)
        {
            for (int i = 0; i < sizeof(p_map) / sizeof(p_map[0]); i++)
            {
                tmp_obj = cJSON_GetObjectItemCaseSensitive(root, p_map[i].name);
                p_map[i].energy->top = cJSON_GetObjectItemCaseSensitive(tmp_obj, "top")->valuedouble;
                p_map[i].energy->peak = cJSON_GetObjectItemCaseSensitive(tmp_obj, "peak")->valuedouble;
                p_map[i].energy->flat = cJSON_GetObjectItemCaseSensitive(tmp_obj, "flat")->valuedouble;
                p_map[i].energy->valley = cJSON_GetObjectItemCaseSensitive(tmp_obj, "valley")->valuedouble;
                p_map[i].energy->total = cJSON_GetObjectItemCaseSensitive(tmp_obj, "total")->valuedouble;
            }
        }
        else
        {
            cJSON_Delete(root);
            root = NULL;
        }
    }

    if (root == NULL)
    {
        root = cJSON_CreateObject();
        cJSON_AddNumberToObject(root, "time", cur_date);
        //
        for (int i = 0; i < sizeof(p_map) / sizeof(p_map[0]); i++)
        {
            tmp_obj = cJSON_CreateObject();
            cJSON_AddNumberToObject(tmp_obj, "top", p_map[i].energy->top);
            cJSON_AddNumberToObject(tmp_obj, "peak", p_map[i].energy->peak);
            cJSON_AddNumberToObject(tmp_obj, "flat", p_map[i].energy->flat);
            cJSON_AddNumberToObject(tmp_obj, "valley", p_map[i].energy->valley);
            cJSON_AddNumberToObject(tmp_obj, "total", p_map[i].energy->total);
            cJSON_AddItemToObject(root, p_map[i].name, tmp_obj);
        }
        char *json = cJSON_Print(root);
        write_file_data(cfg_f, json, strlen(json));
        cJSON_Delete(root);
        cJSON_free(json);
    }
    else
    {
        cJSON_Delete(root);
    }
    free(f_data);
    return 0;
}

static int ele_price_calculate(income_host *h, double *income)
{
    energy *today_energy = &h->today_energy;
    int efficiency = 0;
    month_price *price = h->price;
    ele_energy active_energy = {0};
    ele_energy reactive_energy = {0};

    ele_price *p_tmp = NULL;
    // 获取电量
    if (0 != get_energy(&h->meter, &active_energy, &reactive_energy))
    {
        ems_syslog(LOG_ERR, "get energy error");
        return -1;
    }
    //
    time_t t = time(NULL);
    struct tm local = *localtime(&t);
    if (local.tm_mday != today_energy->time_day) // 新的一天开始了
    {
        if (today_energy->time_day > 0) // 不是第一次启动
        {
            extern int cloud_lnxall_write_history(time_t t);
            cloud_lnxall_write_history(t - 60); // 存前一天时间
        }
        today_energy->time_day = local.tm_mday;
        today_energy->active_energy = active_energy;
        today_energy->reactive_energy = reactive_energy;
        update_today_start_energy(h);
    }

    ems_syslog(LOG_DEBUG, "today_energy->active_energy:[top:%f,peak:%f,flat:%f,valley:%f,total:%f]",
               today_energy->active_energy.top, today_energy->active_energy.peak, today_energy->active_energy.flat, today_energy->active_energy.valley, today_energy->active_energy.total);
    ems_syslog(LOG_DEBUG, "today_energy->reactive_energy:[top:%f,peak:%f,flat:%f,valley:%f,total:%f]",
               today_energy->reactive_energy.top, today_energy->reactive_energy.peak, today_energy->reactive_energy.flat, today_energy->reactive_energy.valley, today_energy->reactive_energy.total);
    // 正向充
    energy_sub(&active_energy, &today_energy->active_energy, &today_energy->active_diff);
    // 反向
    energy_sub(&reactive_energy, &today_energy->reactive_energy, &today_energy->reactive_diff);
    ems_syslog(LOG_NOTICE, "today_energy->active_diff:[top:%f,peak:%f,flat:%f,valley:%f,total:%f]",
               today_energy->active_diff.top, today_energy->active_diff.peak, today_energy->active_diff.flat, today_energy->active_diff.valley, today_energy->active_diff.total);
    ems_syslog(LOG_NOTICE, "today_energy->reactive_energy:[top:%f,peak:%f,flat:%f,valley:%f,total:%f]",
               today_energy->reactive_diff.top, today_energy->reactive_diff.peak, today_energy->reactive_diff.flat, today_energy->reactive_diff.valley, today_energy->reactive_diff.total);
    // 收益
    if (price[local.tm_mon].valid != 0)
    {
        p_tmp = &price[local.tm_mon].price;
        ems_syslog(LOG_DEBUG, "price:[top:%f,peak:%f,flat:%f,valley:%f]", p_tmp->top, p_tmp->peak, p_tmp->flat, p_tmp->valley);
        ems_syslog(LOG_DEBUG, "diff:[top:%f,peak:%f,flat:%f,valley:%f,total:%f]", active_energy.top, active_energy.peak, active_energy.flat, active_energy.valley, active_energy.total);
        h->chgFee = (today_energy->active_diff.top * p_tmp->top + today_energy->active_diff.peak * p_tmp->peak + today_energy->active_diff.flat * p_tmp->flat + today_energy->active_diff.valley * p_tmp->valley);
        h->dischgFee = (today_energy->reactive_diff.top * p_tmp->top + today_energy->reactive_diff.peak * p_tmp->peak + today_energy->reactive_diff.flat * p_tmp->flat + today_energy->reactive_diff.valley * p_tmp->valley);
        if (dev_get_dev_tag_int(DEV_NO_EMS, METER_INVERT) != 0) // 表需要取反
        {
            h->profit = h->dischgFee - h->chgFee;
            efficiency = 1;
        }
        else
        {
            h->profit = h->chgFee - h->dischgFee;
        }
        *income = h->profit;
    }
    else
    {
        *income = 0;
        h->profit = 0;
        ems_syslog(LOG_ERR, "month %d electricity price is invalid", local.tm_mon + 1);
    }
    // 效率
    ems_syslog(LOG_DEBUG, "total:[reactive_diff:%f,active_diff:%f]", today_energy->reactive_diff.total, today_energy->active_diff.total);
    if (efficiency != 0) // 表需要取反
    {
        efficiency = (fabs(today_energy->active_diff.total) > 0.00001) ? today_energy->reactive_diff.total * 1000 / today_energy->active_diff.total : 0;
    }
    else
    {
        efficiency = (fabs(today_energy->reactive_diff.total) > 0.00001) ? today_energy->active_diff.total * 1000 / today_energy->reactive_diff.total : 0;
    }

    ems_syslog(LOG_DEBUG, "today income:%f, efficiency:%d", *income, efficiency);

    return efficiency;
}

int ele_price_init_by_local(income_host *h, const char *cfg_name)
{
    char *pdata = NULL;
    pdata = read_file_data(cfg_name);
    if (pdata == NULL)
    {
        return -1;
    }

    int ret = ele_price_init_by_json_str(h, pdata);
    free(pdata);
    return (ret == 0) ? ret : ret - 10;
}

int ele_price_init_by_remote(income_host *h, const char *cfg_json_str)
{
    int ret = ele_price_init_by_json_str(h, cfg_json_str);
    return (ret == 0) ? ret : ret - 10;
}

// 检查电表是否配置了需要得点位
int income_check_meter(income_host *h)
{
    char nod[DEV_NAME_LEN + TAG_NAME_LEN] = {0};
    const char *dev_no = h->meter.dev_no;
    const char **start = &h->meter.dev_no + 1;
    const char **end = start + (sizeof(h->meter) - sizeof(h->meter.dev_no)) / sizeof(void *);
    while (start < end)
    {
        snprintf(nod, sizeof(nod), "%s.%s", dev_no, *start++);
        if (false == check_nod_exist(nod))
        {
            break;
        }
    }

    if (start < end)
    {
        h->meter_support = 1;
    }
    else
    {
        h->meter_support = 0;
    }

    return 0;
}

int income_update(income_host *h)
{
    double income = 0;

    if (h->meter_support == 0)// 不支持阶梯计费的电表
    {
        return -1;
    }

    int efficiency = ele_price_calculate(h, &income);
    if (efficiency < 0)
    {
        ems_syslog(LOG_ERR, "occur an error in calculating profits");
        return -1;
    }
    else
    {
        dev_set_dev_tag_int(h->dev_no, h->income_tag, (int)(income * 100));
        dev_set_dev_tag_int(h->dev_no, h->efficiency_tag, efficiency);
    }
    return 0;
}
#endif