#include "sprite.h"
#include "camera.h"
#include "utils.h"

typedef struct frame_s
{
    SDL_Rect        rect;
    struct frame_s *next;
} frame_t;

typedef struct animation_s
{
    char               *name;
    int                 frames_sleep;
    frame_t            *list_frames_first;
    frame_t            *list_frames_last;
    frame_t            *current_frame;
    int                 current_frames_sleep;
    int                 number_frame;
    struct animation_s *next;
} animation_t;

static frame_t* _Sprite_CreateFrame(SDL_Rect rect)
{
    frame_t *frame = (frame_t*) malloc(sizeof(frame_t));

    frame->rect = rect;
    frame->next = NULL;

    return frame;
}

static void _Sprite_FreeFrames(frame_t *list_frames_first)
{
    if (list_frames_first == NULL)
        return;

    frame_t *frame = list_frames_first;

    while(frame != NULL)
    {
        frame_t *frame_next = frame->next;
        free(frame);
        frame = frame_next;
    }
}

static animation_t* _Sprite_CreateAnimation(char *name, int frames_sleep)
{
    animation_t *animation = (animation_t*) malloc(sizeof(animation_t));

    animation->name                 = name;
    animation->frames_sleep         = frames_sleep;
    animation->list_frames_first    = NULL;
    animation->list_frames_last     = NULL;
    animation->current_frame        = NULL;
    animation->current_frames_sleep = 0;
    animation->number_frame         = 0;
    animation->next                 = NULL;

    return animation;
}

static void _Sprite_FreeAnimations(animation_t *list_animations_first)
{
    if (list_animations_first == NULL)
        return;

    animation_t *animation = list_animations_first;

    while(animation != NULL)
    {
        animation_t *animation_next = animation->next;
        _Sprite_FreeFrames(animation->list_frames_first);
        free(animation);
        animation = animation_next;
    }
}

static animation_t* _Sprite_GetAnimation(sprite_t* sprite, char *name)
{
    if (sprite == NULL)
        return NULL;

    animation_t *result    = NULL;
    animation_t *animation = sprite->list_animations_first;

    if (animation != NULL)
    {
        while(animation != NULL)
        {
            if (Utils_CompareStrings(animation->name, name, LENGHT_NAME_ANIMATION))
            {
                result = animation;
                break;
            }

            animation = animation->next;
        }
    }

    return result;
}

sprite_t* Sprite_Create(point_t origin, int width, int height, SDL_Texture *texture)
{
    sprite_t *sprite = (sprite_t*) malloc(sizeof(sprite_t));

    sprite->origin                = origin;
    sprite->width                 = width;
    sprite->height                = height;
    sprite->list_animations_first = NULL;
    sprite->list_animations_last  = NULL;
    sprite->current_animation     = NULL;
    sprite->texture               = texture;
    sprite->play                  = false;
    sprite->loop                  = false;
    sprite->flip                  = false;

    return sprite;
}

void Sprite_AnimationAdd(sprite_t* sprite, char *name, int frames_sleep)
{
    if (sprite == NULL)
        return;

    animation_t* find_animation = _Sprite_GetAnimation(sprite, name);

    if (find_animation != NULL)
        return;

    animation_t* animation = _Sprite_CreateAnimation(name, frames_sleep);

    if (sprite->list_animations_first == NULL)
    {
        sprite->list_animations_first = animation;
        sprite->list_animations_last  = animation;
        sprite->current_animation     = animation;
    }
    else
    {
        sprite->list_animations_last->next = animation;
        sprite->list_animations_last       = animation;
    }
}

void Sprite_AnimationAddFrame(sprite_t* sprite, char *name, const SDL_Rect rect)
{
    if (sprite == NULL)
        return;

    animation_t* animation = _Sprite_GetAnimation(sprite, name);

    if (animation == NULL)
        return;

    frame_t* frame = _Sprite_CreateFrame(rect);

    if (animation->list_frames_first == NULL)
    {
        animation->list_frames_first = frame;
        animation->list_frames_last  = frame;
        animation->current_frame     = frame;
    }
    else
    {
        animation->list_frames_last->next = frame;
        animation->list_frames_last       = frame;
    }
}

void Sprite_AnimationPlay(sprite_t* sprite, char *name, bool loop)
{
    if (sprite == NULL)
        return;

    animation_t* find_animation = _Sprite_GetAnimation(sprite, name);

    if (find_animation == NULL)
        return;

    sprite->current_animation = find_animation;
    sprite->current_animation->current_frame        = sprite->current_animation->list_frames_first;
    sprite->current_animation->number_frame         = 0;
    sprite->current_animation->current_frames_sleep = 0;

    sprite->play = true;
    sprite->loop = loop;
}

void Sprite_AnimationStop(sprite_t* sprite)
{
    if (sprite == NULL)
        return;

    if (sprite->current_animation != NULL)
    {
        sprite->current_animation->current_frame = sprite->current_animation->list_frames_first;
        sprite->current_animation->number_frame  = 0;
        sprite->current_animation->frames_sleep  = 0;
        sprite->current_animation->current_frames_sleep = 0;
    }

    sprite->play = false;
}

bool Sprite_IsEndCurrentAnimation(sprite_t* sprite)
{
    bool result = false;

    if (sprite == NULL)
        return false;

    if (sprite->current_animation != NULL)
    {
        if ((sprite->current_animation->current_frame == sprite->current_animation->list_frames_last) &&
            (sprite->current_animation->current_frames_sleep == sprite->current_animation->frames_sleep))
           result = true;
    }

    return result;
}

bool Sprite_IsEndFrameCurrentAnimation(sprite_t* sprite, unsigned int number_frame)
{
    bool result = 0;

    if (sprite == NULL)
        return false;

    if (sprite->current_animation != NULL)
    {
        if ((sprite->current_animation->number_frame == number_frame) &&
            (sprite->current_animation->current_frames_sleep == sprite->current_animation->frames_sleep))
           result = true;
    }

    return result;
}

bool Sprite_IsNameCurrentAnimation(sprite_t* sprite, char *name)
{
    bool result = false;

     if (sprite == NULL)
        return result;

    if (sprite->current_animation != NULL)
    {
        if (Utils_CompareStrings(sprite->current_animation->name, name, LENGHT_NAME_ANIMATION))
            result = true;
    }

    return result;
}

char* Sprite_GetNameCurrentAnimation(sprite_t* sprite)
{
    char *result = "";

    if (sprite == NULL)
        return result;

    if (sprite->current_animation != NULL)
        result = sprite->current_animation->name;

    return result;
}

unsigned int Sprite_GetNumberFrameCurrentAnimation(sprite_t* sprite)
{
    unsigned int result = 0;

    if (sprite == NULL)
        return result;

    if (sprite->current_animation != NULL)
        result = sprite->current_animation->number_frame;

    return result;
}

void Sprite_SetTexture(sprite_t* sprite, SDL_Texture *texture)
{
    if (sprite == NULL)
        return;

    sprite->texture = texture;
}

void Sprite_SetFlip(sprite_t* sprite, bool flip)
{
    if (sprite == NULL)
        return;

    sprite->flip = flip;
}

void Sprite_Update(sprite_t* sprite)
{
    if (sprite == NULL)
        return;

    if (!sprite->play)
        return;

    if (sprite->current_animation != NULL)
    {
        if (sprite->current_animation->current_frame != NULL)
        {
            sprite->current_animation->current_frames_sleep++;

            if (sprite->current_animation->current_frames_sleep > sprite->current_animation->frames_sleep)
            {
                sprite->current_animation->current_frames_sleep = 0;

                frame_t *frame_next = sprite->current_animation->current_frame->next;

                if (frame_next != NULL)
                {
                    sprite->current_animation->current_frame = frame_next;
                    sprite->current_animation->number_frame++;
                }
                else
                {
                    if (!sprite->loop)
                        sprite->play = false;
                    else
                    {
                        sprite->current_animation->current_frame = sprite->current_animation->list_frames_first;
                        sprite->current_animation->number_frame  = 0;
                    }
                }
            }
        }
    }
}

void Sprite_Draw(SDL_Renderer *renderer, sprite_t* sprite, vector_t position)
{
    if (sprite == NULL)
        return;

    SDL_Rect frame_rect;
    SDL_Rect draw_rect;
    bool     draw_frame = false;

    draw_rect.x = (Uint32) position.x - sprite->origin.x;
    draw_rect.y = (Uint32) position.y - sprite->origin.y;
    draw_rect.w = sprite->width;
    draw_rect.h = sprite->height;

    if (sprite->current_animation != NULL)
    {
        if (sprite->current_animation->current_frame != NULL)
        {
            frame_rect = sprite->current_animation->current_frame->rect;
            draw_frame = true;
        }
    }

    if (draw_frame)
    {
        if (sprite->flip)
             Camera_DrawEx(renderer, sprite->texture, &frame_rect, &draw_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
        else Camera_Draw(renderer, sprite->texture, &frame_rect, &draw_rect);
    }
    else
    {
        if (sprite->flip)
             Camera_DrawEx(renderer, sprite->texture, NULL, &draw_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
        else Camera_Draw(renderer, sprite->texture, NULL, &draw_rect);
    }
}

void Sprite_Free(sprite_t* sprite)
{
    if (sprite == NULL)
        return;

    _Sprite_FreeAnimations(sprite->list_animations_first);

    free(sprite);
}
