/*
    zinput.c - Linux input /dev/input/eventX
    Copyright (C) 2018 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

    https://gist.github.com/uobikiemukot/457338b890e96babf60b#file-linux_input-c-L2

*/

#include <libziaint.h>

#include <zinput.h>
#include <zdebug.h>

#include <fcntl.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef Z_HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef Z_HAVE_SDL
#include <SDL.h>
#endif


struct zinput *zinput_open(char *device, struct zselect *zsel){
#ifdef Z_HAVE_LINUX_INPUT_H
    struct zinput *inp = g_new0(struct zinput, 1);
    inp->device = g_strdup(device);
    inp->zsel = zsel;

    inp->fd = open(inp->device, O_RDONLY);

    if (inp->fd < 0) {
        zinput_free(inp);
        return NULL;
    }

    inp->event = g_new0(struct input_event, 1);

    return inp;
#else
	return NULL;
#endif
}

void zinput_free(struct zinput *inp){
#ifdef Z_HAVE_LINUX_INPUT_H
    if (!inp) return;

    if (inp->fd >= 0) {
        zselect_set(inp->zsel, inp->fd, NULL, NULL, NULL, inp);
        close(inp->fd);
    }

    g_free(inp->device);
    g_free(inp->event);
    g_free(inp);
#endif
}

void zinput_calibrate(struct zinput *inp, int swap_xy, int x_res, int y_res, int x_min, int x_max, int y_min, int y_max)
{
    inp->swap_xy = swap_xy;
    inp->x_res = x_res;
    inp->y_res = y_res;
    inp->x_min = x_min;
    inp->x_max = x_max;
    inp->y_min = y_min;
    inp->y_max = y_max;
}

void zinput_register_touchscreen_sdl(struct zinput *inp){
    zselect_set(inp->zsel, inp->fd, zinput_read_handler, NULL, NULL, inp);
    inp->touch_sdl = 1;
}

static int zinput_get_px(struct zinput *inp){
    int val = inp->swap_xy ? inp->abs_y : inp->abs_x;
    int px;

    if (inp->x_min < inp->x_max){
        px = ((inp->abs_x - inp->x_min) * inp->x_res) / (inp->x_max - inp->x_min);
    }else{
        px = ((inp->abs_x - inp->x_max) * inp->x_res) / (inp->x_min - inp->x_max);
    }
    if (px < 0) px = 0;
    if (px >= inp->x_res) px = inp->x_res - 1;
    if (inp->debug) printf("abs=%d -> px=%d\n", val, px);
    return px;
}

static int zinput_get_py(struct zinput *inp){
    int val = inp->swap_xy ? inp->abs_x : inp->abs_y;
    int py;

    if (inp->y_min < inp->y_max){
        py = ((inp->abs_y - inp->y_min) * inp->y_res) / (inp->y_max - inp->y_min);
    }else{
        py = ((inp->y_min - inp->abs_y) * inp->y_res) / (inp->y_min - inp->y_max);
    }
    if (py < 0) py = 0;
    if (py >= inp->y_res) py = inp->y_res - 1;
    if (inp->debug) printf("abs=%d -> py=%d\n", val, py);
    return py;
}

void zinput_read_handler(void *xxx){
#ifdef Z_HAVE_SDL
#ifdef Z_HAVE_LINUX_INPUT_H
    struct zinput *inp = (struct zinput*)xxx;
    SDL_Event ev;


    int ret = read(inp->fd, inp->event, sizeof(struct input_event));
    if (ret < 0) {
        error("Can't read from %s fd %d, closing", inp->device, inp->fd);
        zselect_set(inp->zsel, inp->fd, NULL, NULL, NULL, inp);
        close(inp->fd);
        inp->fd = -1;
    }

    switch(inp->event->type){
        case EV_KEY:
            switch(inp->event->code){
                case BTN_TOUCH:
                    inp->btn_touch = inp->event->value;
                    break;
            }
            break;
        case EV_ABS:
            if (!inp->touch_sdl) break;
            // code, value
            switch(inp->event->code){
                case ABS_X:
                    inp->abs_x = inp->event->value;
                    break;
                case ABS_Y:
                    inp->abs_y = inp->event->value;
                    break;
            }
            break;
        case EV_SYN:
            ev.type = inp->btn_touch ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
            if (inp->btn_touch == inp->old_btn_touch) ev.type = SDL_MOUSEMOTION;
            inp->old_btn_touch = inp->btn_touch;

            ev.button.button = SDL_BUTTON_LEFT;
            ev.button.state = inp->btn_touch ? SDL_PRESSED : SDL_RELEASED;
            ev.button.x = zinput_get_px(inp);
            ev.button.y = zinput_get_py(inp);
            SDL_PushEvent(&ev);
#if 0
            char *txt = "???";
            if (ev.type == SDL_MOUSEMOTION) txt = "MOVE ";
            if (ev.type == SDL_MOUSEBUTTONDOWN) txt = "DOWN ";
            if (ev.type == SDL_MOUSEBUTTONUP) txt = "UP   ";
            printf("-%s  x=%d  y=%d\n", txt, ev.button.x, ev.button.y);
#endif            
            break;
    }
#endif
#endif
}
