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

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

#include "player.h"

#include "old.h"

#define FRAME_STANDING 0
#define FRAME_JUMPING 1
#define WALK_CYCLE_FRAMES 4

static const Rect frames[2 * 4] =
{
    /* right */
    { 0, 80, 144, 144 },
    { 16, 80, 144, 144 },
    { 0, 80, 144, 144 },
    { 32, 80, 144, 144 },

    /* left */
    { 48, 80, 144, 144 },
    { 64, 80, 144, 144 },
    { 48, 80, 144, 144 },
    { 80, 80, 144, 144 },
};

void old_init(Entity *e)
{
    e->frames = (const Rect *)frames;
    e->update = old_update;
}

static uint8_t is_bottom_deadly(uint16_t x, uint16_t y)
{
    while (1)
    {
        if (map_is_deadly(x, y))
            return 1;

        if (map_is_blocked(x, y))
            return 0;

        y += 8;
    }
}

void old_update(Entity *e)
{
    if (e->gravity == GRAVITY_OFF
            && !map_is_blocked(e->x + 4, e->y + 16)
            && !map_is_blocked(e->x + 11, e->y + 16))
    {
        e->gravity = GRAVITY_DOWN;
        e->frame = FRAME_JUMPING;
    }

    if (e->gravity != GRAVITY_OFF)
    {
        uint8_t steps = gravity_seq[e->gravity - 1];

        if (e->gravity > GRAVITY_DOWN)
        {
            /* going down! */
            for (uint8_t i = 0; i < steps; i++)
            {
                /* hit the floor */
                if ((map_is_blocked(e->x + 11, e->y + 16)
                        || map_is_blocked(e->x + 4, e->y + 16))
                        && !map_is_blocked(e->x + 4, e->y + 15)
                        && !map_is_blocked(e->x + 11, e->y + 15))
                {
                    e->gravity = GRAVITY_OFF;
                    e->frame = FRAME_STANDING;
                    e->flags = rand() % (ENEMY_JUMP_DELAY * 2);
                    break;
                }
                e->y++;
            }
        }
        else
        {
            /* going up! */
            if (e->y < steps)
                e->y = 0;
            else
                e->y -= steps;
        }

        if (e->gravity != GRAVITY_OFF && e->gravity != GRAVITY_SEQ_LEN)
            e->gravity++;
    }
    else
    {
        if (e->delay & 1)
        {
            if (e->dir == DIR_RIGHT)
            {
                if (map_is_blocked(e->x + 16, e->y + 15)
                        || is_bottom_deadly(e->x + 16, e->y + 16))
                {
                    e->dir = DIR_LEFT;
                    e->flags++;
                }
                else
                    e->x++;

                if (e->flags > ENEMY_JUMP_DELAY
                        && map_is_blocked(e->x + 7, e->y - 1)
                        && !map_is_blocked(e->x + 7, e->y - 9))
                {
                    e->gravity = GRAVITY_UP;
                    e->frame = FRAME_JUMPING;
                }
            }
            else
            {
                /* dir is LEFT */
                if (map_is_blocked(e->x - 1, e->y + 15)
                        || is_bottom_deadly(e->x - 1, e->y + 16))
                {
                    e->dir = DIR_RIGHT;
                    e->flags++;
                }
                else
                    e->x--;

                if (e->flags > ENEMY_JUMP_DELAY
                        && map_is_blocked(e->x + 7, e->y - 1)
                        && !map_is_blocked(e->x + 7, e->y - 9))
                {
                    e->gravity = GRAVITY_UP;
                    e->frame = FRAME_JUMPING;
                }
            }
        }

        if (e->delay++ == WALK_DELAY)
        {
            e->delay = 0;
            e->frame++;
            if (e->frame == WALK_CYCLE_FRAMES)
                e->frame = FRAME_STANDING;
        }
    }

    if (player_collision(e))
    {
        player_hit();
        /* change direction */
        e->dir ^= 1;
    }
}