#include "neko.h"
#include "common.h"
#include <math.h>

static int texture_width = 0;
static int texture_height = 0;

static float catx = 200;
static float caty = 200;
static float speed = 5.0;

#define TIME_BEFORE_SPLEEP 3000
#define TIME_BEFORE_RUNNING 1000
#define TIME_ANIM_INTERVAL 300

enum CatState {
    ST_SLEEPING,
    ST_RUNNING,
    ST_WAKING_UP,
    ST_STAND_BY,
} cat_state = ST_RUNNING;

enum CatFrames {
    STAND_BY=0,
    WAKING_UP=1,
    UP=2,
    DOWN=4,
    LEFT=6,
    RIGHT=8,
    UP_LEFT=10,
    UP_RIGHT=12,
    DOWN_RIGHT=14,
    DOWN_LEFT=16,
    SLEEPING=18,
};

static int state_timer;
static int anim_timer = 0;
static int current_frame = 0; 

static float diffx, diffy;

static IMG_Animation *anim;
static SDL_Texture **anim_textures;

int neko_init(SDL_Renderer *renderer, SDL_Window *window){
    char *neko_gif_path = NULL;
    
    /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
       engines refer to these as "sprites." We'll do a static texture (upload once, draw many
       times) with data from a bitmap file. */

    /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
       Load a .bmp into a surface, move it to a texture from there. */
    SDL_asprintf(&neko_gif_path, "%sreal_oneko.gif", SDL_GetBasePath());
    anim = IMG_LoadAnimation(neko_gif_path);
    if (!anim) {
        SDL_Log("Couldn't load %s: %s\n", neko_gif_path, SDL_GetError());
        return SDL_APP_FAILURE;
    }
    SDL_free(neko_gif_path);  /* done with this, the file is loaded. */

    texture_width = anim->w;
    texture_height = anim->h;

    anim_textures = (SDL_Texture **)SDL_calloc(anim->count, sizeof(*anim_textures));
    if (!anim_textures) {
        SDL_Log("Couldn't allocate textures\n");
        IMG_FreeAnimation(anim);
        return SDL_APP_FAILURE;
    }
    for (int i = 0; i < anim->count; ++i) {
        anim_textures[i] = SDL_CreateTextureFromSurface(renderer, anim->frames[i]);
    }

    SDL_AddTimer(50, move_cat, window);
}

void neko_render(SDL_Renderer *renderer, SDL_Window *window){
    SDL_FRect dst_rect;

    dst_rect.x = catx;
    dst_rect.y = caty;
    dst_rect.h = WINDOW_HEIGHT/20;
    dst_rect.w = WINDOW_HEIGHT/20;

    int texture_index = 0;
    switch (cat_state)
    {
    case ST_SLEEPING:
        texture_index = SLEEPING+current_frame;
        break;
    case ST_WAKING_UP:
        texture_index = WAKING_UP;
        break;
    case ST_STAND_BY:
        texture_index = STAND_BY;
        break;
    case ST_RUNNING:
        // We do this to know if our cat is moving in diagonals
        double angle = fabs(atan((double) (diffy/diffx)));
        if(angle<SDL_PI_D/3 && angle>SDL_PI_D/6){ // Diagonals
            if(diffy<0){
                if(diffx<0){
                    texture_index = DOWN_RIGHT+current_frame;
                }else{
                    texture_index = DOWN_LEFT+current_frame;
                }
            }else{
                if(diffx<0){
                    texture_index = UP_RIGHT+current_frame;
                }else{
                    texture_index = UP_LEFT+current_frame;
                }
            }
        }else{ // pas diagonals
            if(fabs(diffy) > fabs(diffx)){
                if(diffy<0){
                    texture_index = DOWN+current_frame;
                }else{
                    texture_index = UP+current_frame;
                }
            }else{
                if(diffx<0){
                    texture_index = RIGHT+current_frame;
                }else{
                    texture_index = LEFT+current_frame;
                }
            }
        }
        break;
    
    default:
        break;
    }

    SDL_RenderTexture(renderer, anim_textures[texture_index], NULL, &dst_rect);

    if(SDL_GetTicks()-anim_timer > TIME_ANIM_INTERVAL){
        current_frame = (current_frame+1) % 2;
        anim_timer=SDL_GetTicks();
    }
}

void neko_quit(){
    SDL_DestroyTexture(anim_textures[current_frame]);
}

Uint32 move_cat(void *window, SDL_TimerID timerID, Uint32 interval){
    float mousex, mousey;
    float hypo;
    int windowx, windowy;

    SDL_GetGlobalMouseState(&mousex, &mousey);
    SDL_GetWindowPosition(window, &windowx, &windowy);

    diffx = catx-(mousex-(float)windowx);
    diffy = caty-(mousey-(float)windowy);
    hypo = sqrt(diffx*diffx + diffy*diffy); 
    switch (cat_state)
    {
    case ST_RUNNING:
        if (hypo>50){
            catx -= (diffx*speed/hypo);
            caty -= (diffy*speed/hypo);
        }else{
            cat_state = ST_STAND_BY;
            state_timer = SDL_GetTicks();
        }
        break;
    case ST_STAND_BY:
        if (hypo>50){
            cat_state = ST_RUNNING;
        }else if (SDL_GetTicks()-TIME_BEFORE_SPLEEP > state_timer){
            cat_state = ST_SLEEPING;
        }
        break;
    case ST_SLEEPING:
        if (hypo>50){
            cat_state = ST_WAKING_UP;
            state_timer = SDL_GetTicks();
        }
        break;
    case ST_WAKING_UP:
        if (SDL_GetTicks()-TIME_BEFORE_RUNNING > state_timer){
            cat_state = ST_RUNNING;
        }
        break;
    
    default:
        break;
    }
    return(interval);
}