#include "art/game_player.h"
#include "art/game_tiles.h"
#include "device/device.h"
#include "map/game_map.h"
#include "game.h"
#include "sys/sys.h"

game_bbox game_player_bbox = {
    .x_origin=0x0500,
    .y_origin=0x0c00,
    .x=0x9000,
    .y=0x1000,
    .w=0x0a00,
    .h=0x0c00,
};
bool game_player_grounded = false;
sys_i32 game_player_jump_frames = 0;
sys_i32 game_player_coyote_frames = 0;
sys_i32 game_player_dx = 0;
sys_i32 game_player_dy = 0;
bool game_player_faceleft = false;

typedef enum {
    GAME_PLAYER_ANIM_STANDING,
    GAME_PLAYER_ANIM_WALKING,
    GAME_PLAYER_ANIM_FLOATING,
} game_player_anim;

game_player_anim game_player_anim_state = GAME_PLAYER_ANIM_STANDING;
uint8_t game_player_anim_progress = 0;

void game_player_anim_transition(game_player_anim anim);
void game_player_anim_add_progress(uint8_t amt);

void game_player_init() {
    game_player_bbox.x = game_map_player_start_x;
    game_player_bbox.y = game_map_player_start_y;
}

void game_player_update() {
    game_collision collision = game_collision_move_to_contact(
        &game_player_bbox, 
        game_player_dx, 
        game_player_dy
    );
    
    if (collision.collided_x) { game_player_dx = 0; }
    if (collision.collided_y) { game_player_dy = 0; }

    game_player_grounded = !game_collision_can_move(
        game_player_bbox, 0, 1
    );

    {
        sys_i32 fric_numerator = game_player_grounded ? 0x70 : 0x7f;
        game_player_dx = (
            game_player_dx * fric_numerator + 
            // round up
            0x7f * sys_sgn_i32(game_player_dx)
        ) / 0x80;
        if (sys_abs_i32(game_player_dx) > 0x140) { 
            game_player_dx = sys_sgn_i32(game_player_dx) * 0x140;
        }

        int wanted_dx = game_player_dx;
        if (sys_btn(DEVICE_BUTTON_L)) { wanted_dx -= 0x50; }
        if (sys_btn(DEVICE_BUTTON_R)) { wanted_dx += 0x50; }
    
        // allow this if grounded or if it's not a change of direction
        if (
            /*
            game_player_grounded || 
            (sys_sgn_i32(wanted_dx) == sys_sgn_i32(game_player_dx))
            */
           true
        ) {
            game_player_dx = wanted_dx;
        } else {
            // the smallest possible amount of movement without changing sign
            game_player_dx = sys_sgn_i32(game_player_dx);
        }
    }

    if (game_player_grounded || game_player_coyote_frames > 0) {
        if (sys_btnp(DEVICE_BUTTON_U, false)) { 
            game_player_dy = -0x200; 
            game_player_jump_frames = 13;
            game_player_grounded = false;
        } 
    }

    if (game_player_dx < 0) { game_player_faceleft = true; }
    if (game_player_dx > 0) { game_player_faceleft = false; }

    bool wants_to_go_higher = false;
    if (game_player_grounded) {
        game_player_coyote_frames = 5;  
        if (game_player_dy > 0) { game_player_dy = 0; }
        if (sys_abs_i32(game_player_dx) < 0x20) { game_player_dx = 0; }
    }
    else {
        game_player_coyote_frames = 
            sys_max_i32(0, game_player_coyote_frames - 1);
        wants_to_go_higher = sys_btn(DEVICE_BUTTON_U);
        bool is_going_higher = wants_to_go_higher && game_player_jump_frames > 0;
        if (game_player_dy >= 0 || !is_going_higher) {
            game_player_dy += 48;
        } else {
            game_player_jump_frames -= 1;
        }
    }

    // deduce animation state
    if (game_player_grounded) {
        if (game_player_dx == 0) {
            game_player_anim_transition(GAME_PLAYER_ANIM_STANDING);
        } else {
            game_player_anim_transition(GAME_PLAYER_ANIM_WALKING);
            game_player_anim_add_progress(
                sys_min_i32(sys_abs_i32(game_player_dx) / 0x10, 0x20)
            );
        }
    } else {
        if (wants_to_go_higher) {
            game_player_anim_transition(GAME_PLAYER_ANIM_FLOATING);
        } else {
            game_player_anim_transition(GAME_PLAYER_ANIM_STANDING);
        }
    }
}

void game_player_set_camera() {
    sys_i32 x = game_player_bbox.x;
    sys_i32 y = game_player_bbox.y;

    // TODO: Use round_down et al
    sys_i32 x_pixel = x / PIXEL_SZ_MICROPIXEL;
    sys_i32 y_pixel = y / PIXEL_SZ_MICROPIXEL;
    
    sys_i32 x_topcorner = x_pixel - x_pixel % DEVICE_W;
    sys_i32 y_topcorner = y_pixel - y_pixel % DEVICE_H;

    sys_camera_set(x_topcorner, y_topcorner);
}

void game_player_draw() {
    int game_player_image;
    switch (game_player_anim_state) {
    case GAME_PLAYER_ANIM_WALKING:
        game_player_image = 2 + (game_player_anim_progress / 0x80) * 2;
        break;
    case GAME_PLAYER_ANIM_FLOATING:
        game_player_image = 6;
        break;
    default:
    case GAME_PLAYER_ANIM_STANDING:
        game_player_image = 0;
        break;
    }

    sys_sprite_draw_ext(
        spr_game_player,
        game_player_image,
        game_player_bbox.x / 0x100 - 8,
        game_player_bbox.y / 0x100 - 16, 
        2, 2, 
        game_player_faceleft, false
    );
}

void game_player_anim_transition(game_player_anim anim) {
    if (game_player_anim_state == anim) { return ;}
    game_player_anim_state = anim;
    game_player_anim_progress = 0;
}

void game_player_anim_add_progress(uint8_t amt) {
    game_player_anim_progress = (
        (uint32_t) game_player_anim_progress +
        (uint32_t) amt
    ) & 0xff;
}


void game_player_get_center(sys_i32* x, sys_i32* y) {
    *x = game_player_bbox.x;

    // center on me, not on feet
    *y = game_player_bbox.y - 8 * PIXEL_SZ_MICROPIXEL;  
}