#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
{
	/**
	 * 这个兼容属性可以是 board_name / product
	 * 建议使用 board_name, product 目的是向后兼容
	 */
	const char **compatible;

	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
static char *WLOSANY_LC100_cpt[] = {"WLOSANY_LC100", NULL};
static char *WLOSANY_LC100_node[] = {"led-heart", "led-internet", "led-lora"};
// ECU1170
static char *WLOSANY_ECU1170_cpt[] = {"WLOSANY_ECU1170", NULL};
static char *WLOSANY_ECU1170_node[] = {"PL1", "PL2", "PL3"};
// WLOSANY_MA35D1
static char *WLOSANY_MA35D1_cpt[] = {"WLOSANY_MA35D1", NULL};
static char *WLOSANY_MA35D1_node[] = {"run", "net", "wifi"};

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]),
	},
	{
		.compatible = WLOSANY_ECU1170_cpt,
		.led_node = WLOSANY_ECU1170_node,
		.max = sizeof(WLOSANY_ECU1170_node) / sizeof(WLOSANY_ECU1170_node[0]),
	},
	{
		.compatible = WLOSANY_MA35D1_cpt,
		.led_node = WLOSANY_MA35D1_node,
		.max = sizeof(WLOSANY_MA35D1_node) / sizeof(WLOSANY_MA35D1_node[0]),
	},
};

static const product_leds *get_resource(const char *product_name)
{
	const product_leds *its = NULL;
	int i;

	if (wifi_and_run_led_inversion() < 0)
		return 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);

				// temporary solution for differing case
				if (strstr(product_name, "LC100") != NULL) {
					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) {
		fprintf(stderr, "Error, please call led_init at first");
		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;
}
