#include <mouse.h>
#include <SDL.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

static void mouse_click(const SDL_MouseButtonEvent *const ev,
    struct mouse *const m)
{
    int mask;

    if (!m->first_clicked)
    {
        srand(time(NULL));
        m->first_clicked = true;
    }

    switch (ev->button)
    {
        case 1:
            mask = 1 << MOUSE_BUTTON_LEFT;
            break;

        case 3:
            mask = 1 << MOUSE_BUTTON_RIGHT;
            break;

        default:
            return;
    }

    if (ev->state == SDL_PRESSED)
        m->mask |= mask;
    else
        m->mask &= ~mask;
}

static void mouse_event(const SDL_MouseMotionEvent *const ev,
    struct mouse *const m)
{
    m->x = ev->x;
    m->y = ev->y;
}

void mouse_update(struct mouse *const m)
{
    const short x = m->x, y = m->y;
    SDL_Event ev;
    int n;

    m->oldmask = m->mask;

    while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK
        | SDL_MOUSEBUTTONDOWNMASK | SDL_MOUSEBUTTONUPMASK)) > 0)
    {
        switch (ev.type)
        {
            case SDL_MOUSEBUTTONDOWN:
                /* Fall through. */
            case SDL_MOUSEBUTTONUP:
                mouse_click(&ev.button, m);
                /* SDL_PeepEvents might return both pressed and released
                 * button events on the same cycle, but this library only
                 * expects one event at a time.
                 * Contrarily, all available SDL_MOUSEMOTION events must
                 * be treated simultaneously to avoid latency. */
                goto end;

            case SDL_MOUSEMOTION:
                mouse_event(&ev.motion, m);
                break;

            default:
                fprintf(stderr, "%s: unexpected SDL_Event %d\n",
                    __func__, ev.type);
                break;
        }
    }

    m->dx = m->x - x;
    m->dy = m->y - y;

end:

    if (n < 0)
    {
        fprintf(stderr, "%s: SDL_PeepEvents: %s\n",
            __func__, SDL_GetError());
        return;
    }
}

bool mouse_available(void)
{
    /* Mouse availability is always assumed in SDL. */
    return true;
}
