#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <sys/ioctl.h>

#include <avm/hui/legacy.h>

#define DEVICE_NAME "/dev/led"

static int device_fd = -1;


int led_event_init(int version)
{
    if(version != LED_EVENT_VERSION)
        return -2;
    device_fd = open(DEVICE_NAME, O_RDONLY);
    if( device_fd < 0 )
        return -1;
    return 0;
}

int led_event_deinit(void)
{
    if(device_fd < 0 )
        return -1;
    if(close(device_fd) < 0)
        return -1;
    device_fd = -1;
    return 0;
}


int led_event_set(enum _led_event event, unsigned int value)
{
    struct led_event my_led_event;

    if(device_fd < 0 )
        return -1;
    my_led_event.version = LED_EVENT_VERSION;
    my_led_event.event = event;
    my_led_event.event_value = value;
    my_led_event.event_param = NULL;
    my_led_event.event_param_size = 0;

    if(ioctl(device_fd, LED_IOC_EVENT, &my_led_event) < 0)
        return -1;

    return 0;
}

int led_event(enum _led_event event)
{
    return led_event_set(event, /*value=*/1);
}


int led_event_set_with_event(enum _led_event event, unsigned int value, unsigned int param_len, void *param)
{
    struct led_event my_led_event;

    if((device_fd < 0) || (param == NULL) || (param_len == 0))
        return -1;
    my_led_event.version = LED_EVENT_VERSION;
    my_led_event.event = event;
    my_led_event.event_value = value;
    my_led_event.event_param = param;
    my_led_event.event_param_size = param_len;

    if(ioctl(device_fd, LED_IOC_EVENT_WITH_PARAM, &my_led_event) < 0)
        return -1;

    return 0;
}


int led_event_with_event(enum _led_event event, unsigned int param_len, void *param)
{
    return led_event_set_with_event(event, /*value=*/1, param_len, param);
}

struct avm_hui_event_alias {
	const char *name;
	const struct avm_hui_event_info *info;
};

#define DEFINE_EVENT_STATE(_name, ...) __VA_ARGS__
#define DEFINE_EVENT(_name, _id, ...) \
	const struct avm_hui_event_info __event_info_ ## _name = { \
		.id = _id, \
		.name = #_name, \
	};
#define DEFINE_EVENT_RAW(_name, _id, ...) DEFINE_EVENT(_name, _id)
#define DEFINE_EVENT_ALIAS(_name, _alias)                          \
	const struct avm_hui_event_alias __event_alias_##_name = { \
		.name = #_name,                                    \
		.info = &__event_info_##_alias,                    \
	};
#define _ALL_EVENTS
#include <avm/hui/_events.h>


const struct avm_hui_event_info *_avm_hui_all_events[] = {
#define DEFINE_EVENT_STATE(_name, ...) __VA_ARGS__
#define DEFINE_EVENT(_name, _id, ...) \
	&__event_info_ ## _name,
#define DEFINE_EVENT_RAW(_name, _id, ...) DEFINE_EVENT(_name, _id)
#define _ALL_EVENTS
#include <avm/hui/_events.h>
	NULL,
};

const struct avm_hui_event_alias *_avm_hui_all_event_alias[] = {
#define DEFINE_EVENT_ALIAS(_name, ...) &__event_alias_##_name,
#define _ALL_EVENTS
#include <avm/hui/_events.h>
	NULL,
};

#define foreach_avm_hui_event_alias(_i, _var)                          \
	for (_i = 0, _var = _avm_hui_all_event_alias[0]; _var != NULL; \
	     _i++, _var = _avm_hui_all_event_alias[_i])

const char* avm_hui_get_event_name(enum _led_event event_id) {
	int i;
	const struct avm_hui_event_info *info;

	foreach_avm_hui_event(i, info) {
		if (info->id == event_id)
			return info->name;
	}

	return NULL;
}

const struct avm_hui_event_info* avm_hui_get_info_by_id(enum _led_event id) {
	int i;
	const struct avm_hui_event_info *info;

	foreach_avm_hui_event(i, info) {
		if (info->id == id)
			return info;
	}

	return NULL;
}

const struct avm_hui_event_info* avm_hui_get_info_by_name(const char *name) {
	int i;
	const struct avm_hui_event_info *info;
	const struct avm_hui_event_alias *alias;

	foreach_avm_hui_event(i, info) {
		if (strcmp(info->name, name) == 0)
			return info;
	}

	foreach_avm_hui_event_alias(i, alias) {
		if (strcmp(alias->name, name) == 0)
			return alias->info;
	}

	return NULL;
}