#include "led.h"
#include "dy_common.h"

#ifdef MOXA_VM1220T
  #include <vm1220/vm1220_io.h>
#endif

#define STATE_LED_DEFAULT        0
#define RUN_LED_DEFAULT        1
typedef struct _led_ctrl_t
{
    leds_num_t io;
    int twinkle_ms;
    int timer;
    bool previous_on;
    bool on;                           //灯是否已点亮，初始状态应该是暗的
    bool twinkle_once;
    bool twinkle;
} led_ctrl_t;

typedef struct _led_var_t
{
    char product_name[PRODUCT_NAME_LEN];
    led_ctrl_t net_led;
    led_ctrl_t lora_led;
    led_ctrl_t run_led;
} led_var_t;

typedef struct product
{
    const char **compatible;//这个兼容属性可以是board_name也可以是produt,建议是使用board_name,produt是为了兼容以前
    const char **led_node;
    //
    int max;
} product_leds;

led_var_t var = {0};
const product_leds *leds = NULL;

// WooLink2004-W-4G
const static char *WooLink_cpt[] = {"WooLink", "WOOLINK", "QIYANG", NULL};
const static char *WooLink_node[] = {"led-heart", "led-internet", "led-lora"};
// WLOSANY_EM1000
const static char *WLOSANY_EM1000_cpt[] = {"WLOSANY_EM1000", NULL};
const static char *WLOSANY_EM1000_node[] = {"led1", "led2", "led3", "led4"};
// WLOSANY_RK3568
static char *WLOSANY_RK3568_cpt[] = {"WLOSANY_RK3568", NULL};
static char *WLOSANY_RK3568_node[] = {"run", "net", "wifi"};
// WLOSANY_LC100 -- add by David Zhou
static char *WLOSANY_LC100_cpt[] = {"WLOSANY_LC100", NULL};
static char *WLOSANY_LC100_node[] = {"led-heart", "led-internet", "led-lora"};
static char *LED_PATH = NULL;
static char *WLOSANY_COMMON_PATH="/sys/class/leds";

#ifdef MOXA_VM1220T
static int is_vm1220t;
static const char * WLOSANY_VM1220T = "WLOSANY_VM1220T";
#endif

// WLOSANY_RK3568
int wifi_and_run_led_inversion()
{
    int ret = 0;
    static char sn[16] = {};
    static char *sn1 = "218830000001";
    static char *sn2 = "21883000001F";
    static char *sn3 = "218831000001";
    static char *sn4 = "218831000017";

    ret = get_board_sn(sn);
    if(ret < 0)
    {
   	return -1;
    }
    
    if(((strcmp(sn, sn1) >= 0) && (strcmp(sn, sn2) <= 0)) || ((strcmp(sn, sn3) >= 0) && (strcmp(sn, sn4) <= 0)))
    {
       WLOSANY_RK3568_node[0] = "run";
       WLOSANY_RK3568_node[2] = "wifi";
    }
    else
    {
       WLOSANY_RK3568_node[0] = "wifi";
       WLOSANY_RK3568_node[2] = "run";
    }
    return 0;
}

const product_leds support_product[] = {
    {
        .compatible = WLOSANY_RK3568_cpt,
        .led_node = WLOSANY_RK3568_node,
        .max = sizeof(WLOSANY_RK3568_node) / sizeof(WLOSANY_RK3568_node[0]),
    },
    {
        .compatible = WLOSANY_EM1000_cpt,
        .led_node = WLOSANY_EM1000_node,
        .max = sizeof(WLOSANY_EM1000_node) / sizeof(WLOSANY_EM1000_node[0]),
    },
    {
        .compatible = WooLink_cpt,
        .led_node = WooLink_node,
        .max = sizeof(WooLink_node) / sizeof(WooLink_node[0]),
    },
    {
	.compatible = WLOSANY_LC100_cpt,
	.led_node = WLOSANY_LC100_node,
	.max = sizeof(WLOSANY_LC100_node) / sizeof(WLOSANY_LC100_node[0]),
    }
};

static const product_leds *get_resource(const char *product_name)
{
   int i;
   if(wifi_and_run_led_inversion() < 0)
   {
   	return NULL;
   }
    const product_leds *its = NULL;
    for (i = 0; i < sizeof(support_product) / sizeof(support_product[0]); i++)
    {
        its = &support_product[i];
        const char **compatible = its->compatible;
        while (*compatible != NULL)
        {
            if (strstr(product_name, *compatible) != NULL)
            {
        	dy_syslog(LOG_ERR, "Now product is %s\n", product_name);
		if (strstr(product_name, "LC100") != NULL) {// Added by David Zhou, it's a temporary solution. for differing case
			LED_PATH = WLOSANY_COMMON_PATH;
        		dy_syslog(LOG_ERR, "Now product is LC100, the LED path %s\n", LED_PATH);
		} else {
			LED_PATH = WLOSANY_COMMON_PATH;
        		dy_syslog(LOG_ERR, "Now product is not LC100, the LED path %s\n", LED_PATH);
		}
                return its;
            }
            compatible++;
        }
    }

    return NULL;
}

static void led_set(int io, int state)
{
    char buff[100];
    if (io >= (leds ? leds->max : 4)) {
        return;
    }

#ifdef MOXA_VM1220T
    if (is_vm1220t != 0) {
        uint8_t nst = LED_STATE_DARK;
        switch (state) {
        case LED_LIGHT_ON:
            nst = LED_STATE_GREEN;
            break;

        case LED_LIGHT_OFF:
            nst = LED_LIGHT_OFF;
            break;

        case LED_TWINKLE:
            nst = LED_STATE_GREEN_HEARTBEAT;
            break;

        case LED_TWINKLE_ONCE:
            nst = LED_STATE_GREEN_HEARTBEAT;
            break;

        default:
            break;
        }

        switch (io) {
        case IO_LED_RUN:
            VM1220_Misc_SetUserLedState(LED_CHANNEL_USR1, nst);
            break;

        case IO_LED_NET:
            VM1220_Misc_SetUserLedState(LED_CHANNEL_USR2, nst);
            break;

        case IO_LED_LORA:
            VM1220_Misc_SetUserLedState(LED_CHANNEL_USR3, nst);
            break;

        default:
            break;
        }

        return;
    }
#endif
    if (leds == NULL)
        return;

    sprintf(buff, "%s/%s/brightness", LED_PATH, leds->led_node[io]);
    int fd = open(buff, O_WRONLY);
    write(fd, state ? "1" : "0", 2);
    close(fd);
}

static void led_ctrl(led_ctrl_t *led_ctrl, LED_E el)
{
    if (leds == NULL)
    {
        return;
    }
    //长亮优先，闪烁其次
    if (el == LED_LIGHT_ON)
    {
        if (led_ctrl->twinkle == 1)
        {
            my_timer_set_ms(led_ctrl->timer, 0, 0);
            led_ctrl->twinkle = 0;
        }
        led_ctrl->on = 1;
        led_set(led_ctrl->io, led_ctrl->on);
    }
    else if (el == LED_LIGHT_OFF)
    {
        if (led_ctrl->twinkle == 1)
        {
            my_timer_set_ms(led_ctrl->timer, 0, 0);
            led_ctrl->twinkle = 0;
        }
        led_ctrl->on = 0;
        led_set(led_ctrl->io, led_ctrl->on);
    }
    else if (el == LED_TWINKLE)
    {
        if (led_ctrl->twinkle == 1)
        {
            // 已经有闪烁在执行，忽略后面的
            return;
        }

        if (led_ctrl->timer <= 0)
        {
            led_ctrl->timer = my_timer_create();
        }
        my_timer_set_ms(led_ctrl->timer, led_ctrl->twinkle_ms, led_ctrl->twinkle_ms);

        //保存
        led_ctrl->previous_on = led_ctrl->on;
        led_ctrl->twinkle = 1;
    }
    else if (el == LED_TWINKLE_OFF)
    {
        my_timer_set_ms(led_ctrl->timer, 0, 0);
        led_ctrl->twinkle = 0;
        led_ctrl->on = led_ctrl->previous_on;
        led_set(led_ctrl->io, led_ctrl->on);
    }
    else if (el == LED_TWINKLE_ONCE)
    {
        if (led_ctrl->twinkle_once == 1)
        {
            // 已经有闪烁一次在执行，忽略后面的
            return;
        }
        if (led_ctrl->timer <= 0)
        {
            led_ctrl->timer = my_timer_create();
        }
        my_timer_set_ms(led_ctrl->timer, led_ctrl->twinkle_ms, -1);
        led_ctrl->on = !led_ctrl->on;
        led_set(led_ctrl->io, led_ctrl->on);

        led_ctrl->twinkle_once = 1;
    }
}

void post_lora_led(LED_E el)
{
    if (!strcmp(var.product_name, PRODUCT_WOOLINK_1004))
        return ;
    else
        led_ctrl(&var.lora_led, el);
}

void post_net_led(LED_E el)
{
    if (!strcmp(var.product_name, PRODUCT_WOOLINK_1004))
        led_ctrl(&var.lora_led, el);
    else
        led_ctrl(&var.net_led, el);
}

void post_run_led(LED_E el)
{
    led_ctrl(&var.run_led, el);
}

void set_led(int who, int on_off)
{
    led_set(who, on_off);
}

static int led_timer(led_var_t *var, led_ctrl_t *led_ctrl)
{
    TIMER_CONFIRM(led_ctrl->timer);

    led_ctrl->on = !led_ctrl->on;
    led_set(led_ctrl->io, led_ctrl->on);

    if (led_ctrl->twinkle_once == 1)
    {
        led_ctrl->twinkle_once = 0;
    }
    return 0;
}

static void *led_loop(void *param)
{
    fd_set  rset;
    struct timeval timeout;

    while (1)
    {
        int ret = -1, maxfd = 0;

        SELECT_INIT();
        SELECT_ADD_FD(var.net_led.timer);
        SELECT_ADD_FD(var.lora_led.timer);
        SELECT_ADD_FD(var.run_led.timer);

        timeout.tv_usec = 0;
        timeout.tv_sec = 60;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            int error = errno;
            //此处使用alog可能会引起死锁
            printf("%s_%d errno %d\n", __FUNCTION__, __LINE__, error);

            if (error == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var.net_led.timer > 0 && FD_ISSET(var.net_led.timer, &rset))
            {
                FD_CLR(var.net_led.timer, &rset);
                led_timer(&var, &var.net_led);
            }
            if (var.lora_led.timer > 0 && FD_ISSET(var.lora_led.timer, &rset))
            {
                FD_CLR(var.lora_led.timer, &rset);
                led_timer(&var, &var.lora_led);
            }
            if (var.run_led.timer > 0 && FD_ISSET(var.run_led.timer, &rset))
            {
                FD_CLR(var.run_led.timer, &rset);
                led_timer(&var, &var.run_led);
            }
        }
    }

    return NULL;
}

static int is_luotuo_product(void)
{
    FILE *fp;
    char buffer[100];
    char *cmd = "fw_printenv | grep -i ^product= | awk -F = \'{print $2}\'";
    fp = popen(cmd, "r");
    fgets(buffer, sizeof(buffer), fp);
    pclose(fp);

    dy_syslog(LOG_INFO, "buffer: %s", buffer);
    if (0 == strncmp(buffer, "WL-WG-V", strlen("WL-WG-V"))) {
        return 1;
    } else {
        return 0;
    }
}

int led_init(void)
{
    pthread_t thread_loop;

    if (is_luotuo_product()) {
        dy_syslog(LOG_INFO, "luotuo_product");
        var.net_led.io = IO_LED_RUN;
        var.run_led.io = IO_LED_NET;
    }
    else {
        var.net_led.io = IO_LED_NET;
        var.run_led.io = IO_LED_RUN;
    }

    var.net_led.io = IO_LED_NET;
    var.net_led.on = STATE_LED_DEFAULT;
    var.net_led.twinkle_ms = NET_LED_TWINKLE_MS;
    var.net_led.timer = my_timer_create();

    //var.lora_led.io = IO_LED_LORA;
    var.lora_led.on = STATE_LED_DEFAULT;
    var.lora_led.twinkle_ms = LORA_LED_TWINKLE_MS;
    var.lora_led.timer = my_timer_create();

    var.run_led.io = IO_LED_RUN;
    var.run_led.on = RUN_LED_DEFAULT;
    var.run_led.twinkle_ms = RUN_LED_TWINKLE_MS;
    var.run_led.timer = my_timer_create();
    //
    //
    const char *p_tmp = get_board_name();

#ifdef MOXA_VM1220T
    is_vm1220t = 0;
    if (p_tmp && strcmp(p_tmp, WLOSANY_VM1220T) == 0) {
        int ret = pthread_create(&thread_loop, 0, led_loop, NULL);
        is_vm1220t = -1;
        if (ret == 0)
            pthread_setname_np(thread_loop, "led");
        return 0;
    }
#endif

    dy_syslog(LOG_ERR, " -------------- Now product is %s\n", p_tmp);
    if (p_tmp != NULL)
    {
        snprintf(var.product_name, sizeof(var.product_name), "%s", p_tmp);
        leds = get_resource(var.product_name);
        if (leds != NULL)
        {
            int ret = pthread_create(&thread_loop, 0, led_loop, NULL);
            if (ret == 0)
            {
                pthread_setname_np(thread_loop, "led");
            }
            return ret;
        }
        else
        {
            return -1;
        }
    }
    return -2;
}
