#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <termios.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include "config.h"

static struct termios original_term;
const char * key_names[256] = {
    0
};
static int repeat_counters[256] = {
    1
};
static int current_repeat_key = 0;
static int line_lock = -1;

enum KEY_STATUS {
    KEY_RELEASE = 0,
        KEY_PRESS = 1,
        KEY_REPEAT = 2
};

void usage() {
    printf("Usage: %s <EVENT_FILE_PATH>.\n", PROGRAM_NAME);
}

void version() {
    printf("%s (v%s)\n", PROGRAM_FULL_NAME, PROGRAM_VERSION);
    printf("%s\n", PROGRAM_AUTHORS);

}

void init_key_names() {
    key_names[KEY_RESERVED] = "KEY_RESERVED";
    key_names[KEY_Q] = "Q";
    key_names[KEY_W] = "W";
    key_names[KEY_E] = "E";
    key_names[KEY_R] = "R";
    key_names[KEY_T] = "T";
    key_names[KEY_Y] = "Y";
    key_names[KEY_U] = "U";
    key_names[KEY_I] = "I";
    key_names[KEY_O] = "O";
    key_names[KEY_P] = "P";
    key_names[KEY_A] = "A";
    key_names[KEY_S] = "S";
    key_names[KEY_D] = "D";
    key_names[KEY_F] = "F";
    key_names[KEY_G] = "G";
    key_names[KEY_H] = "H";
    key_names[KEY_J] = "J";
    key_names[KEY_K] = "K";
    key_names[KEY_L] = "L";
    key_names[KEY_Z] = "Z";
    key_names[KEY_X] = "X";
    key_names[KEY_C] = "C";
    key_names[KEY_V] = "V";
    key_names[KEY_B] = "B";
    key_names[KEY_N] = "N";
    key_names[KEY_M] = "M";

    key_names[KEY_1] = "1";
    key_names[KEY_2] = "2";
    key_names[KEY_3] = "3";
    key_names[KEY_4] = "4";
    key_names[KEY_5] = "5";
    key_names[KEY_6] = "6";
    key_names[KEY_7] = "7";
    key_names[KEY_8] = "8";
    key_names[KEY_9] = "9";
    key_names[KEY_0] = "0";

    key_names[KEY_MINUS] = "-";
    key_names[KEY_EQUAL] = "=";
    key_names[KEY_LEFTBRACE] = "[";
    key_names[KEY_RIGHTBRACE] = "]";
    key_names[KEY_SEMICOLON] = ";";
    key_names[KEY_APOSTROPHE] = "'";
    key_names[KEY_GRAVE] = "`";
    key_names[KEY_BACKSLASH] = "\\";
    key_names[KEY_COMMA] = ",";
    key_names[KEY_DOT] = ".";
    key_names[KEY_SLASH] = "/";
    key_names[KEY_SPACE] = "<SPACE>";

    key_names[KEY_ESC] = "<ESC>";
    key_names[KEY_BACKSPACE] = "<BACKSPACE>";
    key_names[KEY_TAB] = "<TAB>";
    key_names[KEY_ENTER] = "<ENTER>";
    key_names[KEY_LEFTSHIFT] = "<L_SHIFT>";
    key_names[KEY_RIGHTSHIFT] = "<R_SHIFT>";
    key_names[KEY_LEFTCTRL] = "<L_CTRL>";
    key_names[KEY_RIGHTCTRL] = "<R_CTRL>";
    key_names[KEY_LEFTALT] = "<L_ALT>";
    key_names[KEY_RIGHTALT] = "<R_ALT>";
    key_names[KEY_LEFTMETA] = "<L_META>";
    key_names[KEY_RIGHTMETA] = "<R_META>";
    key_names[KEY_CAPSLOCK] = "<CAPSLOCK>";
    key_names[KEY_NUMLOCK] = "<NUMLOCK>";
    key_names[KEY_SCROLLLOCK] = "<SCROLLLOCK>";

    key_names[KEY_F1] = "<F1>";
    key_names[KEY_F2] = "<F2>";
    key_names[KEY_F3] = "<F3>";
    key_names[KEY_F4] = "<F4>";
    key_names[KEY_F5] = "<F5>";
    key_names[KEY_F6] = "<F6>";
    key_names[KEY_F7] = "<F7>";
    key_names[KEY_F8] = "<F8>";
    key_names[KEY_F9] = "<F9>";
    key_names[KEY_F10] = "<F10>";
    key_names[KEY_F11] = "<F11>";
    key_names[KEY_F12] = "<F12>";
    key_names[KEY_F13] = "<F13>";
    key_names[KEY_F14] = "<F14>";
    key_names[KEY_F15] = "<F15>";
    key_names[KEY_F16] = "<F16>";
    key_names[KEY_F17] = "<F17>";
    key_names[KEY_F18] = "<F18>";
    key_names[KEY_F19] = "<F19>";
    key_names[KEY_F20] = "<F20>";
    key_names[KEY_F21] = "<F21>";
    key_names[KEY_F22] = "<F22>";
    key_names[KEY_F23] = "<F23>";
    key_names[KEY_F24] = "<F24>";

    key_names[KEY_KP0] = "<KP0>";
    key_names[KEY_KP1] = "<KP1>";
    key_names[KEY_KP2] = "<KP2>";
    key_names[KEY_KP3] = "<KP3>";
    key_names[KEY_KP4] = "<KP4>";
    key_names[KEY_KP5] = "<KP5>";
    key_names[KEY_KP6] = "<KP6>";
    key_names[KEY_KP7] = "<KP7>";
    key_names[KEY_KP8] = "<KP8>";
    key_names[KEY_KP9] = "<KP9>";
    key_names[KEY_KPDOT] = "<KP.>";
    key_names[KEY_KPCOMMA] = "<KP,>";
    key_names[KEY_KPENTER] = "<KPENTER>";
    key_names[KEY_KPMINUS] = "<KP->";
    key_names[KEY_KPPLUS] = "<KP+>";
    key_names[KEY_KPSLASH] = "<KP/>";
    key_names[KEY_KPASTERISK] = "<KP*>";
    key_names[KEY_KPEQUAL] = "<KP=>";

    key_names[KEY_UP] = "<UP>";
    key_names[KEY_DOWN] = "<DOWN>";
    key_names[KEY_LEFT] = "<LEFT>";
    key_names[KEY_RIGHT] = "<RIGHT>";
    key_names[KEY_HOME] = "<HOME>";
    key_names[KEY_END] = "<END>";
    key_names[KEY_PAGEUP] = "<PAGEUP>";
    key_names[KEY_PAGEDOWN] = "<PAGEDOWN>";
    key_names[KEY_INSERT] = "<INSERT>";
    key_names[KEY_DELETE] = "<DELETE>";

    key_names[KEY_MUTE] = "<MUTE>";
    key_names[KEY_VOLUMEDOWN] = "<VOLUMEDOWN>";
    key_names[KEY_VOLUMEUP] = "<VOLUMEUP>";
    key_names[KEY_POWER] = "<POWER>";
    key_names[KEY_SLEEP] = "<SLEEP>";
    key_names[KEY_WAKEUP] = "<WAKEUP>";
    key_names[KEY_PLAY] = "<PLAY>";
    key_names[KEY_PAUSE] = "<PAUSE>";
    key_names[KEY_PLAYPAUSE] = "<PLAYPAUSE>";
    key_names[KEY_STOPCD] = "<STOP>";
    key_names[KEY_PREVIOUSSONG] = "<PREVIOUS>";
    key_names[KEY_NEXTSONG] = "<NEXT>";
    key_names[KEY_REWIND] = "<REWIND>";
    key_names[KEY_FASTFORWARD] = "<FASTFORWARD>";
    key_names[KEY_RECORD] = "<RECORD>";
    key_names[KEY_EJECTCD] = "<EJECT>";

    key_names[KEY_SYSRQ] = "<SYSRQ>";
    key_names[KEY_PAUSE] = "<PAUSE>";
    key_names[KEY_MENU] = "<MENU>";
    key_names[KEY_CALC] = "<CALC>";
    key_names[KEY_MAIL] = "<MAIL>";
    key_names[KEY_WWW] = "<WWW>";
    key_names[KEY_SEARCH] = "<SEARCH>";
    key_names[KEY_BACK] = "<BACK>";
    key_names[KEY_FORWARD] = "<FORWARD>";
    key_names[KEY_REFRESH] = "<REFRESH>";
    key_names[KEY_STOP] = "<STOP>";
    key_names[KEY_COPY] = "<COPY>";
    key_names[KEY_CUT] = "<CUT>";
    key_names[KEY_PASTE] = "<PASTE>";
    key_names[KEY_UNDO] = "<UNDO>";
    key_names[KEY_REDO] = "<REDO>";
    key_names[KEY_FIND] = "<FIND>";
    key_names[KEY_HELP] = "<HELP>";
    key_names[KEY_FILE] = "<FILE>";
    key_names[KEY_SAVE] = "<SAVE>";
    key_names[KEY_NEW] = "<NEW>";
    key_names[KEY_OPEN] = "<OPEN>";
    key_names[KEY_CLOSE] = "<CLOSE>";
    key_names[KEY_EXIT] = "<EXIT>";
    key_names[KEY_PRINT] = "<PRINT>";
    key_names[KEY_BRIGHTNESSDOWN] = "<BRIGHTNESSDOWN>";
    key_names[KEY_BRIGHTNESSUP] = "<BRIGHTNESSUP>";
    key_names[KEY_DISPLAY_OFF] = "<DISPLAYOFF>";
    key_names[KEY_MICMUTE] = "<MICMUTE>";
    key_names[KEY_WLAN] = "<WLAN>";
    key_names[KEY_BLUETOOTH] = "<BLUETOOTH>";
}

void disable_term_echo() {
    struct termios new_setting, init_setting;
    tcgetattr(0, & original_term);
    new_setting = original_term;

    new_setting.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, & new_setting);
}

void enable_term_echo() {
    tcsetattr(0, TCSANOW, & original_term);
}

void print_pure(struct input_event * ie) {
    if (repeat_counters[ie -> code] != 1) {
        printf("%-15s x%-13d\r", key_names[ie -> code], repeat_counters[ie -> code]);
    } else {
        printf("%s", key_names[ie -> code]);
    }

    fflush(stdout);
}

void print_with_time(struct input_event * ie) {
    struct tm * tm_info;
    char time_buf[64];
    time_t sec = (time_t) ie -> input_event_sec;
    tm_info = localtime( & sec);
    strftime(time_buf, sizeof(time_buf), "%F %T", tm_info);

    if (repeat_counters[ie -> code] != 1) {
        printf("%s.%06ld: %s x%d\n", time_buf, (long) ie -> time.tv_usec, key_names[ie -> code], repeat_counters[ie -> code]);
    } else {
        printf("%s.%06ld: %s\n", time_buf, (long) ie -> time.tv_usec, key_names[ie -> code]);

    }

    return;
}

int main(int argc, char ** argv) {
    if (argv[1] != NULL && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) {
        usage();
        return -1;
    } else if (argv[1] != NULL && (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v")) == 0) {
        version();
        return -1;
    }

    setvbuf(stdout, NULL, _IONBF, 0);
    disable_term_echo();

    struct config * cfg = config_init();
    if (!cfg) {
        return -1;
    }

    printf("Device: %s\n", cfg -> device);

    struct input_event ie;
    int fd = open(cfg -> device, O_RDONLY);

    if (fd == -1 && errno == EACCES) {
        printf("Permission denied: please run %s with sudo.\n", PROGRAM_NAME);
        return -1;
    }

    if (fd == -1 && errno == ENOENT) {
        printf("No such file or directory: %s\n", cfg -> device);
        return -1;

    }

    init_key_names();

    void( * fp)(struct input_event * ie) = (cfg -> enable_time == true) ? print_with_time : print_pure;

    while (1) {
        read(fd, & ie, sizeof(ie));
        if (ie.type == EV_KEY) {

            if (ie.value == KEY_PRESS) {
                if (line_lock != -1 && !cfg -> enable_time) printf("\n");
                repeat_counters[ie.code] = 1;
				line_lock = ie.code;
                fp(&ie);
            }

            if (ie.value == KEY_REPEAT) {
				if (cfg->enable_time) {
					if (cfg->enable_time) printf("\r\033[K");
				}
                line_lock = ie.code;
                repeat_counters[ie.code]++;
                fp(&ie);
            }
        }

    }

    enable_term_echo();
    return 0;
}
