#include <sys/select.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "dy_utils/dy_common.h"

#define DEV_BEEP "/dev/qiyang_buzzer"
#define DEV_GPIO "/dev/qiyang_imx6_gpio"
#define MOTOR_MAGIC 'L'
#define BUZZER_ON _IOW(MOTOR_MAGIC, 0, int)
#define BUZZER_OFF _IOW(MOTOR_MAGIC, 1, int)
#define IOCTL_BZR_BEEP _IOW('Q', 0x31, int)

#define KEY_STATE_DOWN 1
#define KEY_CILCK_TIME_MIN 20
#define KEY_CILCK_TIME_MAX 500

#define RST_BUTTON_EVENT "/dev/input/event1" //for board which use gpio-keys in DTS
#define IMX_GPIO_NR(bank, nr) (((bank)-1) * 32 + (nr))

/* ioctl cmd */
#define IOCTL_GPIO_LEVEL_GET _IOW('Q', 0x02, int)     //get gpio state
#define IOCTL_GPIO_LEVEL_SET _IOR('Q', 0x03, int)     //set gpio output state
#define IOCTL_GPIO_DIR_INPUT_SET _IOW('Q', 0x04, int) //set gpio as a input

#define SELECT_FCUN_PIN IMX_GPIO_NR(1, 2)
#define USER_KEY1 IMX_GPIO_NR(4, 10)

#define FUNC_REBOOT 1
#define FUNC_KEY 0

static const char *board_name = NULL;

typedef struct _main_var_t
{
    int32_t userkeys_timer;
    int32_t key_fd;
    uint8_t key_last_state;
    uint8_t key_cur_state;
    int64_t key_last_time_stamp;
} main_var_t;

typedef struct
{
    int gpio_index;
    int gpio_level;
} IOCTL_GPIO_ARG_ST;

void reboot_board(int i)
{
    system("reboot");
}

int set_key_func(int func)
{
    int fd = -1;
    printf("set key func\n");

    fd = open(DEV_GPIO, O_RDWR);
    if (fd < 0)
    {
        dy_syslog(LOG_ERR, "open gpio device %s error!", DEV_GPIO);
        return -1;
    }

    IOCTL_GPIO_ARG_ST arg;
    arg.gpio_index = SELECT_FCUN_PIN;
    arg.gpio_level = func;

    if (ioctl(fd, IOCTL_GPIO_LEVEL_SET, &arg) == 0)
    {
        dy_syslog(LOG_ERR, "set gpio func is '%s'", func ? "reboot" : "key");
    }

    close(fd);

    return 0;
}

void set_beep(int loop, int delay_ms)
{
    int i, fd = -1;

    if (strcmp(board_name, BOARD_QIYANG) != 0)
    {
        return;
    }

    fd = open(DEV_BEEP, O_RDWR);
    if (fd < 0)
    {
        return;
    }

    for (i = 0; i < loop * 2; i++)
    {
        int cmd = (i % 2 == 0) ? BUZZER_ON : BUZZER_OFF;
        int delay = (i % 2 == 0) ? 200 : delay_ms;
        ioctl(fd, cmd, cmd);
        usleep(delay * 600);
    }
    close(fd);
}

static int init_userkey(main_var_t *var)
{
    board_name = get_board_name();

    if (strcmp(board_name, BOARD_QIYANG) == 0)
    {
        set_key_func(FUNC_KEY);
    }

    var->key_fd = open(RST_BUTTON_EVENT, O_RDONLY);
    if (var->key_fd < 0)
    {
        dy_syslog(LOG_ERR, "open event device %s error!", RST_BUTTON_EVENT);
    }
    return 0;
}

static int get_userkey(main_var_t *var)
{
    int ret = -1;
    struct input_event t;
    static int key = -1;

    if (read(var->key_fd, &t, sizeof(t)) == sizeof(t))
    {
        if (t.type == EV_KEY)
        {
            ret = t.value; //按下是1，抬起是0
            key = ret;
        }
        else if (t.type == 0)
        {
            //返回上次的值
            ret = key;
        }
    }

    return ret;
}

void userkey_timeout(main_var_t *var)
{
    int key = var->key_cur_state;

    my_timer_delete(var->userkeys_timer);
    var->userkeys_timer = 0;
    //如果现在还是按下的状态，则执行reset config的逻辑
    if (key != -1 && KEY_STATE_DOWN == key)
    {
        dy_syslog(LOG_DEBUG, "*************************************");
        dy_syslog(LOG_DEBUG, "****** Gateway will go to factory *********");
        dy_syslog(LOG_DEBUG, "*************************************");
        // 恢复出厂设置
        system("cp -f /app/config_bak/* /app/config");
        system("cp -f /etc/defcfg/* /etc/config");
        system("cp -f /etc/defcfg/shadow /etc/shadow");
        var->key_last_time_stamp = clock_get_ms();
        char cmd[64] = {0};
        sprintf(cmd, "echo %lld > /app/reset_ts.txt; sync", var->key_last_time_stamp);
        system(cmd);
        set_beep(3, 300);
        sync();
        reboot_board(1);
    }
}

void userkey_chanage(main_var_t *var)
{
    int key = get_userkey(var);

    if (key != -1)
    {
        var->key_cur_state = key;
        if (key == var->key_last_state)
        {
            return;
        }
        long long curr_time_stamp = clock_get_ms();
        var->key_last_state = key;
        if (KEY_STATE_DOWN == key)
        {
            var->key_last_time_stamp = clock_get_ms();
            var->userkeys_timer = my_timer_create();
            if (var->userkeys_timer > 0)
            {
                my_timer_set(var->userkeys_timer, 20, -1);
            }
        }
        else
        {
            my_timer_delete(var->userkeys_timer);
            var->userkeys_timer = 0;
            if ((curr_time_stamp > var->key_last_time_stamp + KEY_CILCK_TIME_MIN) && (curr_time_stamp < var->key_last_time_stamp + KEY_CILCK_TIME_MAX))
            {
                dy_syslog(LOG_INFO, "user key click");
                reboot_board(1);
            }
        }
    }
}

int main_loop()
{
    int ret = -1, maxfd = 0;
    fd_set rset;
    struct timeval timeout;
    main_var_t var = {0};
    init_userkey(&var);

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var.userkeys_timer);
        SELECT_ADD_FD(var.key_fd);
        timeout.tv_usec = 0;
        timeout.tv_sec = 1;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var.userkeys_timer > 0 && FD_ISSET(var.userkeys_timer, &rset))
            {
                FD_CLR(var.userkeys_timer, &rset);
                userkey_timeout(&var);
            }
            if (var.key_fd > 0 && FD_ISSET(var.key_fd, &rset))
            {
                FD_CLR(var.key_fd, &rset);
                userkey_chanage(&var);
            }
        }
    }
}

int main(int argc, char *argv[])
{
    main_loop();
    return 0;
}
