/*
 * ============================================================================
 *  Title:    Mouse Support via SDL
 *  Author:   J. Zbiciak
 * ============================================================================
 *
 *  jzIntv doesn't really support mice at this time.  This module is a
 *  simple module to convert xrel/yrel events into direction events, and
 *  capture mouse button events similarly to joystick buttons.
 *
 *  It should be considered highly experimental.
 *
 * ============================================================================
 */

#include "config.h"
#include "sdl_jzintv.h"
#include "periph/periph.h"
#include "pads/pads.h"
#include "event/event.h"
#include "event/event_tbl.h"
#include "mouse/mouse.h"


static int    last_x = -1, last_y = -1;    /* yes, ugly & evil static vars */
static int    last_dir = -1;
static double to_release = 0;

/* ======================================================================== */
/*  MOUSE_DECODE_MOTION                                                     */
/* ======================================================================== */
LOCAL void mouse_decode_motion
(
    SDL_Event *ev,
    int       *ev_updn, uint_32 *ev_num,
    int       *ex_updn, uint_32 *ex_num
)
{
    int x, y, dx, dy, mag, dir;

    x = ev->motion.x;
    y = ev->motion.y;

    if (last_x == -1)
    {
        last_x = x;
        last_y = y;

        return;
    }

    dx  = last_x - x;
    dy  = last_y - y;
    mag = dx * dx + dy * dy;

    if (mag < 16)   /* about 4 pixels in any direction before a dir event */
        return;

    /* For now just decode 4 directions.  Later, decode 16 like joy does. */
    dir = abs(dx) > abs(dy) ? ( dx > 0 ? EVENT_MOUSE_W : EVENT_MOUSE_E )
        :                     ( dy > 0 ? EVENT_MOUSE_N : EVENT_MOUSE_S );

    if (dir != last_dir)
    {
        if (last_dir != -1)
        {
            *ev_updn = UP;   *ev_num = last_dir;
            *ex_updn = DOWN; *ex_num = dir;
        } else
        {
            *ev_updn = DOWN; *ev_num = dir;
        }
    }

    last_x   = x;
    last_y   = y;
    last_dir = dir;

    /* Release mouse after 0.1 seconds, unless a new event comes in */
    to_release = get_time() + 0.1;

    return;
}

/* ======================================================================== */
/*  MOUSE_DECODE_BUTTON                                                     */
/* ======================================================================== */
LOCAL void mouse_decode_button(SDL_Event *ev, uint_32 *ev_num)
{
    if (ev->button.button > 31)
    {
        *ev_num = EVENT_IGNORE;
        return;
    }

    *ev_num = EVENT_MOUSE_BTN_00 + ev->button.button;
    return;
}


/* ======================================================================== */
/*  MOUSE_DECODE_EVENT -- Pull apart an SDL_EVENT and turn it into our      */
/*                        internal event numbers.                           */
/* ======================================================================== */
void mouse_decode_event(SDL_Event *ev, int *ev_updn, uint_32 *ev_num,
                                       int *ex_updn, uint_32 *ex_num)
{
    *ev_num = EVENT_IGNORE;
    *ex_num = EVENT_IGNORE;

    switch (ev->type)
    {
        case SDL_MOUSEMOTION:
        {
            mouse_decode_motion(ev, ev_updn, ev_num, ex_updn, ex_num);
            break;
        }

        case SDL_MOUSEBUTTONDOWN:
        {
            *ev_updn = DOWN;
            mouse_decode_button(ev, ev_num);
            break;
        }

        case SDL_MOUSEBUTTONUP:
        {
            *ev_updn = UP;
            mouse_decode_button(ev, ev_num);
            break;
        }

        default: *ev_num = EVENT_IGNORE; break;
    }
}

/* ======================================================================== */
/*  MOUSE_PUMP          -- Decide whether to send a mouse up event          */
/* ======================================================================== */
void mouse_pump(int *ev_updn, uint_32 *ev_num)
{
    if (last_dir != -1 && get_time() > to_release)
    {
        *ev_num  = last_dir;
        *ev_updn = UP;
        last_dir = -1;
    } else
    {
        *ev_num  = EVENT_IGNORE;
    }
}
