#include "../proto_forward.h"
#include <sys/syslog.h>
#include <time.h>
#include "../adapter/pe_adapter.h"

static inline int common_adapter_init(pe_class* this_p, void* par)
{
    return 0;
}

static inline int common_set_power(pe_class* this_p, double power)
{
    return adapter_function_set(this_p, FUNCTION_SET_POWER, power);
}

static inline int common_set_repower(pe_class* this_p, double power)
{
    return adapter_function_set(this_p, FUNCTION_SET_REPOWER, power);
}

static inline int common_set_mode(pe_class* this_p, int mode)
{
    return adapter_function_set(this_p, FUNCTION_SET_MODE, mode);
}

static inline int common_set_onoff(pe_class* this_p, int onoff)
{
    return adapter_function_set(this_p, FUNCTION_SET_ONOFF, onoff);
}

static inline int common_set_connect(pe_class* this_p, int connect)
{
    return adapter_function_set(this_p, FUNCTION_SET_CONNECT, connect);
}

static inline int common_get_power(pe_class* this_p, double* power)
{
    return adapter_function_get_double(this_p, FUNCTION_GET_POWER, power);
}

static inline int common_get_repower(pe_class* this_p, double* repower)
{
    return adapter_function_get_double(this_p, FUNCTION_GET_REPOWER, repower);
}

static inline int common_get_mode(pe_class* this_p, int* mode)
{
    int t = 0;
    int ret = adapter_function_get_int(this_p, FUNCTION_GET_MODE, &t);
    *mode   = t;
    return ret;
}

static inline int common_get_onoff(pe_class* this_p, int* onoff)
{
    int t = 0;
    int ret = adapter_function_get_int(this_p, FUNCTION_GET_ONOFF, &t);
    *onoff   = t;
    return ret;
}

static inline int common_get_connect(pe_class* this_p, int* connect)
{
    int t = 0;
    int ret = adapter_function_get_int(this_p, FUNCTION_GET_CONNECT, &t);
    *connect   = t;
    return ret;
}

static inline int common_set_value(pe_class* this_p, const char* function, double value)
{
    return adapter_function_set(this_p, function, value);
}

static inline int common_get_int_value(pe_class* this_p, const char* function, int* value)
{
    return adapter_function_get_int(this_p, function, value);
}

static inline int common_get_double_value(pe_class* this_p, const char* function, double* value)
{
    return adapter_function_get_double(this_p, function, value);
}

static const pe_adapter * next_adapter(const pe_adapter * cpa)
{
    unsigned long cpaddr;

    if (sizeof(*cpa) > PE_ADAPTER_ALIGN) {
        ems_syslog(LOG_ERR, "Error, invalid pe_adapter size and alignment!");
        return NULL;
    }

    cpaddr = (unsigned long) cpa;
    cpaddr += sizeof(*cpa);

    if (cpaddr & (PE_ADAPTER_ALIGN - 1))
        cpaddr = (cpaddr & ~(PE_ADAPTER_ALIGN - 1)) + PE_ADAPTER_ALIGN;
    return (const pe_adapter *) cpaddr;
}

//
static const pe_adapter *get_pe_by_name(char *adp_attr, int *p_compatible_id)
{
    extern const pe_adapter __start_pe_adapter;
    extern const pe_adapter __stop_pe_adapter; // 连接器引入

    const pe_adapter *pcs_adp_start = &__start_pe_adapter;
    const pe_adapter *pcs_adp_stop = &__stop_pe_adapter;
    *p_compatible_id = 0;
    while (pcs_adp_start < pcs_adp_stop)
    {
        char **pp = pcs_adp_start->compatible;
        if (pp == NULL) {
            pcs_adp_start = next_adapter(pcs_adp_start);
            continue;
        }

        while (*pp != NULL)
        {
            ems_syslog(LOG_DEBUG, "compatible = %s", *pp);
            if (strcmp(*pp, adp_attr) == 0)
            {
                *p_compatible_id = pp - pcs_adp_start->compatible;
                return pcs_adp_start;
            }
            pp++;
        }

        pcs_adp_start = next_adapter(pcs_adp_start);
    }

    return NULL;
}
static void pe_adapter_init(pe_class* this_p);

static int pe_probe(pe_class **dev_arr)
{
    int probe_counter = 0;
    int compatible_index = 0;
    proto_forward_t* dev_var = get_proto_forward_var();
    
    for (int i = 0; i < dev_var->channels_size; i++)
    {
        probe_counter += dev_var->channels[i]->devs_size;
    }
    *dev_arr = (pe_class*)malloc(sizeof(pe_class) * probe_counter);
    pe_class* pe_arr = *dev_arr;
    probe_counter = 0;
    if (pe_arr == NULL)
    {
        ems_syslog(LOG_ERR, "malloc failed");
        return -1;
    }

    for (int i = 0; i < dev_var->channels_size; i++)
    {
        ems_syslog(LOG_DEBUG, "channel:%s", dev_var->channels[i]->channel);
        for (int j = 0; j < dev_var->channels[i]->devs_size; j++)
        {
            device_t *dev = dev_var->channels[i]->devs[j];
            template_t *p = dev->use_template;
            ems_syslog(LOG_DEBUG, "dev: %s->template info:model->%s, adapter->%s", dev->no, p->model, p->adapter);
            const pe_adapter *adp = get_pe_by_name(p->adapter, &compatible_index); // 根据 info.adapter 获取适配器

            pe_arr[probe_counter].no = strdup(dev->no);
            pe_arr[probe_counter].opt = (pe_adapter*)malloc(sizeof(pe_adapter));
            memset(pe_arr[probe_counter].opt, 0 , sizeof(pe_adapter));
            if (adp != NULL) 
            {
                ems_syslog(LOG_NOTICE, "dev: %s get adapter success, compatible_index = %d", dev->no, compatible_index);
                memcpy((void*)(pe_arr[probe_counter].opt), adp, sizeof(pe_adapter));
            }
            else
            {
                ems_syslog(LOG_NOTICE, "dev: %s not find adapter, used common adapter", dev->no);
            }
            pe_adapter_init(&pe_arr[probe_counter]);
            pe_arr[probe_counter].compatible_index = compatible_index;
            probe_counter++;
        }
    }
    return probe_counter;
}

/***********************************************************************************************/
static struct pe_adapter_group
{
    pe_class *pe;
    int sum;
} pe_adapter_group;

// 销毁适配器组
void pe_destory_adapter_group(void)
{
    for (int i = 0; i < pe_adapter_group.sum; i++)
    {
        if (pe_adapter_group.pe[i].opt != NULL)
        {
            free(pe_adapter_group.pe[i].opt);
        }
    }
    free(pe_adapter_group.pe);
    pe_adapter_group.pe = NULL;
    pe_adapter_group.sum = 0;
}

static pe_class *pe_get_obj_by_name(const char *pe_no)
{
    pe_class *pe = NULL;
    for (int i = 0; i < pe_adapter_group.sum; i++)
    {
        if (strcmp(pe_no, pe_adapter_group.pe[i].no) == 0)
        {
            pe = &pe_adapter_group.pe[i];
            break;
        }
    }

    return pe;
}

// int *pe_set_connect_all(const char *pe_tag, void *value)
// {
//     pe_class *pe = NULL;
//     for (int i = 0; i < pe_adapter_group.sum; i++)
//     {
        
//         pe = &pe_adapter_group.pe[i];
//         pe->opt->set_connect(pe, *(int *)value);
//     }
//     return 0;

// }

static void pe_adapter_init(pe_class* this_p)
{
    if (this_p->opt->init == NULL) this_p->opt->init = &common_adapter_init;
    if (this_p->opt->set_power == NULL) this_p->opt->set_power = &common_set_power;
    if (this_p->opt->set_repower == NULL) this_p->opt->set_repower = &common_set_repower;
    if (this_p->opt->set_mode == NULL) this_p->opt->set_mode = &common_set_mode;
    if (this_p->opt->set_onoff == NULL) this_p->opt->set_onoff = &common_set_onoff;
    if (this_p->opt->set_connect == NULL) this_p->opt->set_connect = &common_set_connect;
    if (this_p->opt->get_power == NULL) this_p->opt->get_power = &common_get_power;
    if (this_p->opt->get_repower == NULL) this_p->opt->get_repower = &common_get_repower;
    if (this_p->opt->get_mode == NULL) this_p->opt->get_mode = &common_get_mode;
    if (this_p->opt->get_onoff == NULL) this_p->opt->get_onoff = &common_get_onoff;
    if (this_p->opt->get_connect == NULL) this_p->opt->get_connect = &common_get_connect;

    if (this_p->opt->get_int_value == NULL) this_p->opt->get_int_value = &common_get_int_value;
    if (this_p->opt->get_double_value == NULL) this_p->opt->get_double_value = &common_get_double_value;
    if (this_p->opt->set_value == NULL) this_p->opt->set_value = &common_set_value;
}

// 该函数依赖设备和模板的解析结果，所以必须在所有模板加载完成后调用
int pe_adapter_probe(void)
{
    pe_adapter_group.sum = pe_probe(&pe_adapter_group.pe);

    ems_syslog(LOG_NOTICE, "pe probe adapter sum=%d", pe_adapter_group.sum);
    return pe_adapter_group.sum;
}

int pe_set_power(const char *dev_id, double val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_power(pe, val))
    {
        ems_syslog(LOG_ERR, "%s set power failed", pe->no);
        return -2;
    }

    return 0;
}
int pe_set_repower(const char *dev_id, double val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_repower(pe, val))
    {
        ems_syslog(LOG_ERR, "%s set repower failed", pe->no);
        return -2;
    }

    return 0;
}

int pe_set_onoff(const char *dev_id, int val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_onoff(pe, val))
    {
        ems_syslog(LOG_ERR, "%s set onoff failed", pe->no);
        return -2;
    }

    return 0;
}

int pe_set_mode(const char *dev_id, int val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_mode(pe, val))
    {
        ems_syslog(LOG_ERR, "%s set mode failed", pe->no);
        return -2;
    }
    return 0;
}

int pe_set_connect(const char *dev_id, int val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_connect(pe, val))
    {
        ems_syslog(LOG_ERR, "%s set connect failed", pe->no);
        return -2;
    }

    return 0;
}

int pe_get_onoff(const char *dev_id)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    int val = -3;
    if (0 != pe->opt->get_onoff(pe, &val))
    {
        ems_syslog(LOG_ERR, "%s get onoff failed", pe->no);
        return -2;
    }

    return val;
}

int pe_get_power(const char *dev_id, double *val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->get_power(pe, val))
    {
        ems_syslog(LOG_ERR, "%s get power failed", pe->no);
        return -2;
    }

    return 0;
}

int pe_get_repower(const char *dev_id, double *val)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->get_repower(pe, val))
    {
        ems_syslog(LOG_ERR, "%s get repower failed", pe->no);
        return -2;
    }
    return 0;
}

int pe_get_connect(const char *dev_id)
{
    pe_class *pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    int val = -3;
    if (0 != pe->opt->get_connect(pe, &val))
    {
        ems_syslog(LOG_ERR, "%s get connect failed", pe->no);
        return -2;
    }

    return val;
}

int pe_get_mode(const char* dev_id)
{
    pe_class* pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    int val = -3;
    if (0 != pe->opt->get_mode(pe, &val))
    {
        ems_syslog(LOG_ERR, "%s get connect failed", pe->no);
        return -2;
    }

    return val;
}

// 指定function的set 和 get
int pe_set_value(const char* dev_id, const char* function, double val)
{
    pe_class* pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->set_value(pe, function, val))
    {
        ems_syslog(LOG_NOTICE, "%s get value %s failed", pe->no, function);
        return -2;
    }
    return 0;
}

int pe_get_int_value(const char* dev_id, const char* function, int* val)
{
    pe_class* pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->get_int_value(pe, function, val))
    {
        ems_syslog(LOG_NOTICE, "%s get value %s failed", pe->no, function);
        return -2;
    }

    return 0;
}

int pe_get_double_value(const char* dev_id, const char* function, double* val)
{
    pe_class* pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        ems_syslog(LOG_ERR, "pe get obj failed,dev_no:%s", dev_id);
        return -1;
    }

    if (0 != pe->opt->get_double_value(pe, function, val))
    {
        ems_syslog(LOG_NOTICE, "%s get value %s failed", pe->no, function);
        return -2;
    }

    return 0;
}


extern int adapter_function_exist(pe_class* this_p, const char* function);

int pe_function_exist(const char* dev_id, const char* function)
{
    pe_class* pe = pe_get_obj_by_name(dev_id);
    if (pe == NULL)
    {
        return -1;
    }
    if (adapter_function_exist(pe, function))
        return 0;
    else
        return -1;
}
