#include <stdint.h>
#include <stdlib.h>

#include "vga.h"
#include "sound.h"
#include "entities.h"
#include "map.h"
#include "game.h"

#include "player.h"
#include "effect.h"

#include "pickup.h"

#define MAX_FRAME 3
#define MAX_BONUS_FRAME 4

#define MAX_TTL ((uint16_t)800)

typedef enum
{
    PICKUP_TIME = 0,
    PICKUP_BONUS,
    PICKUP_PICKAXE,
    PICKUP_GOLDKEY,
    PICKUP_SILVERKEY,
} PickupType;

static const Rect frames_in[2 * 4] =
{
    { 96, 16, 144, 112 },
    { 128, 16, 144, 112 },
    { 112, 16, 144, 112 },

    /* not used */
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 }
};

static const Rect frames[2 * 4] =
{
    /* time */
    { 80, 16, 144, 112 },

    /* pickaxe */
    { 64, 16, 144, 112 },

    /* gold key */
    { 32, 16, 144, 112 },

    /* siver key */
    { 48, 16, 144, 112 },

    /* not used */
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 }
};

static const Rect frames_bonus0[2 * 4] =
{
    { 0, 48, 144, 112 },
    { 16, 48, 144, 112 },
    { 32, 48, 144, 112 },
    { 48, 48, 144, 112 },

    /* not used */
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 }
};

static const Rect frames_bonus1[2 * 4] =
{
    { 0, 64, 144, 112 },
    { 16, 64, 144, 112 },
    { 32, 64, 144, 112 },
    { 48, 64, 144, 112 },

    /* not used */
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 }
};

static const Rect frames_bonus2[2 * 4] =
{
    { 64, 64, 144, 112 },
    { 80, 64, 144, 112 },
    { 96, 64, 144, 112 },
    { 112, 64, 144, 112 },

    /* not used */
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 },
    { 0, 0, 144, 112 }
};

static const Rect *frames_bonuses[] =
{
    frames_bonus0, frames_bonus1, frames_bonus2
};

void pickup_time_init(Entity *e)
{
    e->used = USED_BG;
    e->frames = (const Rect *)frames_in;
    e->flags = PICKUP_TIME;
    e->counter = MAX_TTL * 2 + (rand() % MAX_TTL);
    e->update = pickup_wait_update;
}

void pickup_bonus_init(Entity *e)
{
    e->used = USED_BG;
    e->frames = (const Rect *)frames_in;
    e->flags = PICKUP_BONUS;
    e->counter = MAX_TTL + (rand() % MAX_TTL);
    e->update = pickup_wait_update;
}

void pickup_pickaxe_init(Entity *e)
{
    e->used = USED_BG;
    e->frames = (const Rect *)frames_in;
    e->flags = PICKUP_PICKAXE;
    e->counter = MAX_TTL / 2 + (rand() % MAX_TTL);
    e->update = pickup_wait_update;
}

void pickup_goldkey_init(Entity *e)
{
    e->used = USED_BG;
    e->frames = (const Rect *)frames_in;
    e->flags = PICKUP_GOLDKEY;
    e->counter = 2 + (rand() % MAX_TTL);
    e->update = pickup_wait_update;
}

void pickup_silverkey_init(Entity *e)
{
    e->used = USED_BG;
    e->frames = (const Rect *)frames_in;
    e->flags = PICKUP_SILVERKEY;
    e->counter = 2 + (rand() % MAX_TTL);
    e->update = pickup_wait_update;
}

void pickup_wait_update(Entity *e)
{
    if (e->counter-- == 0)
    {
        e->update = pickup_in_update;
        sound_play_efx(EFX_WARP);
    }
}

void pickup_in_update(Entity *e)
{
    if (e->delay++ == WALK_DELAY - 2)
    {
        e->delay = 0;
        e->frame++;
        if (e->frame == MAX_FRAME)
        {
            switch (e->flags)
            {
                case PICKUP_TIME:
                    e->frames = (const Rect *)frames;
                    e->frame = 0;
                    break;
                case PICKUP_BONUS:
                    e->frames = (const Rect *)frames_bonuses[rand() % 3];
                    e->frame = 0;
                    break;
                case PICKUP_PICKAXE:
                    e->frames = (const Rect *)frames;
                    e->frame = 1;
                    break;
                case PICKUP_GOLDKEY:
                    e->frames = (const Rect *)frames;
                    e->frame = 2;
                    break;
                case PICKUP_SILVERKEY:
                    e->frames = (const Rect *)frames;
                    e->frame = 3;
                    break;
            }
            e->counter = 0;
            e->update = pickup_update;
        }
    }
}

void pickup_update(Entity *e)
{
    /* the keys NEVER disappear */
    if (e->flags != PICKUP_GOLDKEY && e->flags != PICKUP_SILVERKEY
            && e->counter++ == MAX_TTL)
    {
        effect_out_init(e);
        sound_play_efx(EFX_WARP);
        return;
    }

    /* the bonuses have an animation */
    if (e->flags == PICKUP_BONUS)
    {
        e->delay++;
        /* first frame lasts longer */
        if ((e->frame == 0 && e->delay == WALK_DELAY * 4)
                || (e->frame != 0 && e->delay == WALK_DELAY - 2))
        {
            e->delay = 0;
            e->frame++;
            if (e->frame == MAX_BONUS_FRAME)
                e->frame = 0;
        }
    }

    if (player_collision_pickup(e))
    {
        switch (e->flags)
        {
            case PICKUP_TIME:
                reset_time();
                sound_play_efx(EFX_PICKUP);
                break;
            case PICKUP_BONUS:
                add_score(250);
                sound_play_efx(EFX_PICKUP);
                break;
            case PICKUP_PICKAXE:
                add_pickaxe();
                sound_play_efx(EFX_PICKUP);
                break;
            case PICKUP_GOLDKEY:
                map_open_goldkey();
                sound_play_efx(EFX_WARP);
                break;
            case PICKUP_SILVERKEY:
                map_open_silverkey();
                sound_play_efx(EFX_WARP);
                break;
        }
        e->used = USED_FREE;
    }
}