#include <camera.h>
#include <camera_private.h>
#include <input.h>
#include <pad.h>
#include <peripheral.h>
#include <gfx.h>
#include <util.h>
#include <stdbool.h>
#include <stdlib.h>

static void cursor_update(struct camera *const cam,
    const struct pad *const p, const struct input *const in)
{
    struct cursor *const c = &cam->cursor;
    enum {STEP = 4};

    if (c->screen.last_w != screen_w
        || c->screen.last_h != screen_h)
        cursor_init(c);

    if (input_pad_pressed(in, p, PAD_KEY_LEFT)
        && (c->x - STEP)
        && (!cam->x || c->x != c->x_init))
        c->x -= STEP;
    else if (input_pad_pressed(in, p, PAD_KEY_RIGHT)
        && (c->x + STEP < screen_w)
        && (c->x != c->x_init || cam->x <= -cam->dim.w))
        c->x += STEP;

    if (input_pad_pressed(in, p, PAD_KEY_UP)
        && (c->y - STEP)
        && (c->y != c->y_init || !cam->y))
        c->y -= STEP;
    else if (input_pad_pressed(in, p, PAD_KEY_DOWN)
        && (c->y + STEP < screen_h)
        && (c->y != c->y_init || cam->y <= -cam->dim.h))
        c->y += STEP;

    c->state = pad_pressed(p, PAD_KEY_A) ||
        pad_pressed(p, PAD_KEY_B) ?
        CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE;
}

static void update_speed(struct camera *const cam,
    const struct pad *const p, const struct input *const in)
{
    enum
    {
        MAX_SPEED = 10,
        STEP = 1,
        T_STEP = 2
    };

    const struct cursor *const c = &cam->cursor;

    if (c->x == c->x_init
        && (!cam->x_speed || ++cam->xt >= T_STEP))
    {
        if (input_pad_pressed(in, p, PAD_KEY_RIGHT))
        {
            if (cam->x_speed > 0)
                cam->x_speed = -STEP;
            else if (cam->x_speed - STEP >= -MAX_SPEED)
                cam->x_speed -= STEP;
        }
        else if (input_pad_pressed(in, p, PAD_KEY_LEFT))
        {
            if (cam->x_speed < 0)
                cam->x_speed = STEP;
            else if (cam->x_speed + STEP <= MAX_SPEED)
                cam->x_speed += STEP;
        }
        else
            cam->x_speed = 0;

        cam->xt = 0;
    }
    else if (c->x != c->x_init)
            cam->x_speed = 0;

    if (c->y == c->y_init
        && (!cam->y_speed || ++cam->yt >= T_STEP))
    {
        if (input_pad_pressed(in, p, PAD_KEY_DOWN))
        {
            if (cam->y_speed > 0)
                cam->y_speed = STEP;
            else if (cam->y_speed - STEP >= -MAX_SPEED)
                cam->y_speed -= STEP;
        }
        else if (input_pad_pressed(in, p, PAD_KEY_UP))
        {
            if (cam->y_speed < 0)
                cam->y_speed = -STEP;
            else if (cam->y_speed + STEP <= MAX_SPEED)
                cam->y_speed += STEP;
        }
        else
            cam->y_speed = 0;

        cam->yt = 0;
    }
    else if (c->y != c->y_init)
        cam->y_speed = 0;
}

static enum pad_key get_ref_key(struct cursor_pos_rt *const rt,
    const size_t ref)
{
    enum pad_key key;

    const struct cursor_pos *const cur = &rt->list[rt->i],
        *const next = &rt->list[ref];
    const short nx = next->x - cur->x,
        ny = next->y - cur->y;

    if (abs(nx) > abs(ny))
        key = nx > 0 ? PAD_KEY_RIGHT : PAD_KEY_LEFT;
    else
        key = ny > 0 ? PAD_KEY_DOWN : PAD_KEY_UP;

    return key;
}

static void cursor_update_fixed(struct camera *const cam,
    const struct pad *const p, const struct input *const in)
{
    struct cursor *const c = &cam->cursor;
    struct cursor_pos_rt *const rt = &c->rt;
    const size_t next = rt->i + 1;

    if (next < rt->n)
    {
        const enum pad_key key = get_ref_key(rt, next);

        if (input_pad_justpressed(in, p, key))
            rt->i = next;
    }
    else if (rt->i)
    {
        const size_t prev = rt->i - 1;
        const enum pad_key key = get_ref_key(rt, prev);

        if (input_pad_justpressed(in, p, key))
            rt->i = prev;
    }

    const struct cursor_pos *const cur = &rt->list[rt->i];

    c->x = cur->x;
    c->y = cur->y;
}

void camera_update_pad(struct camera *const cam, const struct pad *const p,
    const struct input *const in)
{
    if (cam->cursor.rt.list)
        cursor_update_fixed(cam, p, in);
    else
    {
        cursor_update(cam, p, in);
        update_speed(cam, p, in);
        camera_update_pos(cam);
    }
}
