#include <gui.h>
#include <gui/button.h>
#include <gui_button_private.h>
#include <gui_private.h>
#include <camera.h>
#include <gfx.h>
#include <input.h>
#include <mouse.h>
#include <pad.h>
#include <peripheral.h>

static int render(const struct gui_common *const g)
{
    static int (*const f[])(const struct gui_button *) =
    {
        [GUI_BUTTON_TYPE_1] = gui_button_render_type1,
        [GUI_BUTTON_TYPE_SPRITE] = gui_button_render_sprite
    };

    const struct gui_button *const b = (const struct gui_button *)g;

    return f[b->type](b);
}

static void get_dim(const struct gui_common *const g,
    short *const w, short *const h)
{
    static void (*const f[])(const struct gui_button *, short *, short *) =
    {
        [GUI_BUTTON_TYPE_1] = gui_button_get_dim_type1,
        [GUI_BUTTON_TYPE_SPRITE] = gui_button_get_dim_sprite
    };

    const struct gui_button *const b = (const struct gui_button *)g;

    f[b->type](b, w, h);
}

static bool pressed(const struct gui_button *const b,
    const union peripheral *const p,
    const struct camera *const cam,
    const struct input *const in)
{
    short w, h;

    get_dim(&b->common, &w, &h);
    return gui_pressed(&b->common, in, p, cam, w, h);
}

static int update(struct gui_common *const g,
    const union peripheral *const p, const struct camera *const c,
    struct input *const in)
{
    struct gui_button *const b = (struct gui_button *)g;

    if (pressed(b, p, c, in) && b->on_pressed)
        b->on_pressed(b->arg);

    return 0;
}

void gui_button_init(struct gui_button *const b, const enum gui_button_type t)
{
    static const struct gui_common_cb cb =
    {
        .get_dim = get_dim,
        .update = update,
        .render = render
    };

    *b = (const struct gui_button)
    {
        .common =
        {
            .cb = &cb
        },

        .type = t
    };

    static void (*const f[])(struct gui_button *) =
    {
        [GUI_BUTTON_TYPE_1] = gui_button_init_type1,
        [GUI_BUTTON_TYPE_SPRITE] = gui_button_init_sprite
    };

    f[b->type](b);
}
