#include <camera.h>
#include <camera_private.h>
#include <gfx.h>
#include <mouse.h>
#include <pad.h>
#include <util.h>
#include <limits.h>
#include <stdbool.h>

struct sprite cursor_sprite;

bool cursor_collision(const struct camera *const cam, const struct util_rect *const d)
{
    unsigned long x, y;

    cursor_pos(cam, &x, &y);

    const struct util_rect cd =
    {
        .x = x,
        .y = y,
        .w = CAMERA_CURSOR_WIDTH / 4,
        .h = CAMERA_CURSOR_HEIGHT / 4
    };

    return util_collision(d, &cd);
}

void cursor_pos(const struct camera *const cam, unsigned long *const x,
    unsigned long *const y)
{
    const struct cursor *const c = &cam->cursor;

    *x = c->x - cam->x;
    *y = c->y - cam->y;
}

int cursor_render(const struct cursor *const c)
{
    sprite_get_or_ret(s, -1);

    if (sprite_clone(&cursor_sprite, s))
    {
        fprintf(stderr, "%s: sprite_clone failed\n", __func__);
        return -1;
    }

    s->x = c->x;
    s->y = c->y;
    s->w = CAMERA_CURSOR_WIDTH;
    s->h = CAMERA_CURSOR_HEIGHT;

    switch (c->state)
    {
        case CURSOR_STATE_IDLE:
            break;

        case CURSOR_STATE_HOVERING:
            s->u += CAMERA_CURSOR_WIDTH;
            break;

        case CURSOR_STATE_PRESSED:
            s->u += CAMERA_CURSOR_WIDTH * 2;
            break;
    }

    const int ret = sprite_sort(s);

    if (ret)
        fprintf(stderr, "%s: sprite_sort failed\n", __func__);

    return ret;
}

void cursor_init(struct cursor *const c)
{
    const unsigned x = (screen_w / 2) - CAMERA_CURSOR_WIDTH,
        y = (screen_h / 2) - CAMERA_CURSOR_HEIGHT;

    *c = (const struct cursor)
    {
        .x = x,
        .x_init = x,
        .y = y,
        .y_init = y,
        .screen =
        {
            .last_w = screen_w,
            .last_h = screen_h
        }
    };
}

void camera_update_pos(struct camera *const cam)
{
    const struct camera_dim *const d = &cam->dim;

    if (!d->w || !d->h)
        return;

    cam->x += cam->x_speed;

    if (cam->x > 0)
        cam->x = 0;
    else if (cam->x < -d->w + screen_w)
        cam->x = -d->w + screen_w;

    cam->y += cam->y_speed;

    if (cam->y > 0)
        cam->y = 0;
    else if (cam->y < -d->h + screen_h)
        cam->y = -d->h + screen_h;
}

bool camera_translate(const struct camera *const cam, const struct util_rect *const dim,
    short *const x, short *const y)
{
    const long tx = dim->x + cam->x, ty = dim->y + cam->y;

    if (!gfx_inside_drawenv(tx, ty, dim->w, dim->h))
        return false;

    *x = tx;
    *y = ty;
    return true;
}

void cursor_set_pos_list(struct cursor *const c,
    const struct cursor_pos *const pos, const size_t n)
{
    c->rt = (const struct cursor_pos_rt)
    {
        .list = pos,
        .n = n
    };
}

void camera_update(struct camera *const cam,
    const union peripheral *const p, const struct input *const in)
{
    switch (p->common.type)
    {
        case PERIPHERAL_TYPE_PAD:
            camera_update_pad(cam, &p->pad.pad, in);
            break;

        case PERIPHERAL_TYPE_KEYBOARD_MOUSE:
            camera_update_mouse(cam, &p->kbm.mouse);
            break;

        case PERIPHERAL_TYPE_TOUCH:
            camera_update_touch(cam, &p->kbm.mouse, in);
            break;
    }
}
