#include <building.h>
#include <gfx.h>
#include <human_player.h>
#include <player.h>
#include <unit.h>
#include <gui.h>
#include <gui/bar.h>
#include <gui/button.h>
#include <gui/container.h>
#include <gui/label.h>
#include <gui/progress_bar.h>
#include <gui/rounded_rect.h>
#include <terrain.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

enum {X_OFF = 8, Y_OFF = 8};

static void on_item_pressed(void *const arg)
{
    bool *const show = arg;

    *show ^= true;
}

static void on_add_car(void *const arg)
{
    struct human_player *const h = arg;
    struct human_player_item *const it = &h->item;
    const struct unit_cfg cfg =
    {
        .type = UNIT_CFG_TYPE_CAR,
        .x = rand() % (MAP_W - 32),
        .y = rand() % (MAP_H - 32),
    };

    player_create_unit(&cfg, &h->pl);
    it->show = false;
}

static void on_add_walker(void *const arg)
{
    struct human_player *const h = arg;
    struct human_player_item *const it = &h->item;
    const struct unit_cfg cfg =
    {
        .type = UNIT_CFG_TYPE_WALKER,
        .x = rand() % (MAP_W - 32),
        .y = rand() % (MAP_H - 32),
    };

    player_create_unit(&cfg, &h->pl);
    it->show = false;
}

static void on_btn_exit(void *const arg)
{
    struct human_player *const h = arg;

    h->periph->common.exit = true;
}

static void update_items(struct human_player *const h)
{
    struct human_player_item *const it = &h->item;

    {
        struct gui_rounded_rect *const r = &it->rr;

        gui_rounded_rect_init(r);
        r->common.hcentered = r->common.vcentered = true;
        r->adjust = true;
    }

    {
        struct gui_container *const c = &it->topcnt;

        gui_container_init(c);
        c->common.vcentered = true;
        c->common.hcentered = true;
        c->mode = GUI_CONTAINER_MODE_V;
        c->spacing = 4;
        gui_add_child(&it->rr.common, &c->common);
    }

    {
        struct gui_button *const b = &it->add_car;

        gui_button_init(b, GUI_BUTTON_TYPE_1);
        b->common.hcentered = true;
        b->u.type1.w = 80;
        b->u.type1.label.text = "Add car";
        b->on_pressed = on_add_car;
        b->arg = h;
        gui_add_child(&it->topcnt.common, &b->common);
    }

    {
        struct gui_button *const b = &it->add_walker;

        gui_button_init(b, GUI_BUTTON_TYPE_1);
        b->u.type1.w = 80;
        b->common.hcentered = true;
        b->u.type1.label.text = "Add walker";
        b->on_pressed = on_add_walker;
        b->arg = h;
        gui_add_child(&it->topcnt.common, &b->common);
    }

    {
        struct gui_button *const b = &it->exit;

        gui_button_init(b, GUI_BUTTON_TYPE_1);
        b->u.type1.w = 80;
        b->common.hcentered = true;
        b->u.type1.label.text = "Exit";
        b->on_pressed = on_btn_exit;
        b->arg = h;
        gui_add_child(&it->topcnt.common, &b->common);
    }

    struct camera cam =
    {
        .cursor = h->cam.cursor
    };

    gui_update(&it->rr.common, h->periph, &cam, &h->in);
}

void human_player_gui_update(struct human_player *const h)
{
    struct human_player_item *const it = &h->item;
    struct gui_button *const b = &it->show_btn;

    gui_button_init(b, GUI_BUTTON_TYPE_SPRITE);
    b->on_pressed = on_item_pressed;
    b->arg = &it->show;
    b->u.sprite.s = &gui_button_sprites[GUI_BUTTON_SMALL];
    b->common.x = screen_w - b->u.sprite.s->w;

    if (it->show)
        update_items(h);

    struct camera cam =
    {
        .cursor = h->cam.cursor
    };

    gui_update(&b->common, h->periph, &cam, &h->in);
}

static int render_sel_single_building(const struct human_player *const h,
    const struct sel_instance *const sel, struct gui_common *const r)
{
    const struct building *const b = sel->d.b;
    struct gui_label bl;

    gui_label_init(&bl);
    bl.common.x = X_OFF;
    bl.common.y = Y_OFF;
    bl.text = building_str(b);
    gui_add_child(r, &bl.common);

    return gui_render(r);
}

static int render_sel_single_unit(const struct human_player *const h,
    const struct sel_instance *const sel, struct gui_common *const r)
{
    const struct unit *const u = sel->d.u;

    struct gui_label ul;

    gui_label_init(&ul);
    ul.common.hcentered = true;
    ul.common.vcentered = true;
    ul.text = u->name;
    gui_add_child(r, &ul.common);

    return gui_render(r);
}

static int render_sel_single(const struct human_player *const h,
    struct gui_common *const r)
{
    for (size_t i = 0; i < sizeof h->sel / sizeof *h->sel; i++)
    {
        const struct sel_instance *const sel = &h->sel[i];

        if (sel->d.i)
        {
            switch (sel->type)
            {
                case INSTANCE_TYPE_BUILDING:
                    return render_sel_single_building(h, sel, r);

                case INSTANCE_TYPE_UNIT:
                    return render_sel_single_unit(h, sel, r);
            }
        }
    }

    fprintf(stderr, "%s: unreachable\n", __func__);
    return -1;
}

static int render_sel_multiple(const struct human_player *const h,
    struct gui_common *const r)
{
    struct gui_label l;
    char str[sizeof "4294967295 units selected"];
    const int rs = snprintf(str, sizeof str, "%zu units selected", h->n_sel);

    if (rs < 0 || rs >= sizeof str)
        return -1;

    gui_label_init(&l);
    l.common.x = X_OFF;
    l.common.y = Y_OFF;
    l.text = str;
    gui_add_child(r, &l.common);

    return gui_render(r);
}

static int render_top(const struct human_player *const h)
{
    return gui_render(&h->item.show_btn.common);
}

static int render_sel(const struct human_player *const h)
{
    const short wlim = 120;
    struct gui_rounded_rect r;
    short exp_w;

    gui_rounded_rect_init(&r);
    r.common.hcentered = true;
    exp_w = screen_w - 32;
    r.w = exp_w > wlim ? wlim : exp_w;
    r.h = 40;
    r.r = 10;
    r.g = 137;
    r.b = 225;
    r.common.y = screen_h - r.h;

    return h->n_sel == 1 ? render_sel_single(h, &r.common)
        : render_sel_multiple(h, &r.common);
}

static int render_items(const struct human_player *const h)
{
    const struct human_player_item *const it = &h->item;

    return gui_render(&it->rr.common);
}

int human_player_gui_render(const struct human_player *const h)
{
    if (render_top(h))
        return -1;
    else if (h->n_sel && render_sel(h))
        return -1;
    else if (h->item.show && render_items(h))
        return -1;

    return 0;
}
