#include "arc/graphics/sprite.h"

#include "sprite.h"
#include "spritesheet.h"
#include "renderer.h"
#include "arc/math/point.h"
#include "arc/math/rectangle.h"
#include "arc/std/errno.h"
#include <stdlib.h>

void ARC_Sprite_Create(ARC_Sprite **sprite, ARC_Spritesheet *spritesheet, ARC_Array *frames){
    *sprite = (ARC_Sprite *)malloc(sizeof(ARC_Sprite));
    (*sprite)->spritesheet = spritesheet;
    (*sprite)->frames = frames;
    (*sprite)->frameIndex = (uint32_t *)malloc(sizeof(uint32_t));
    *(*sprite)->frameIndex = 0;
    (*sprite)->opacity = 255;
}

void ARC_Sprite_Destroy(ARC_Sprite *sprite){
    free(sprite);
}

void ARC_Sprite_Copy(ARC_Sprite **newSprite, ARC_Sprite *oldSprite){
    *newSprite = (ARC_Sprite *)malloc(sizeof(ARC_Sprite));
    (*newSprite)->spritesheet = oldSprite->spritesheet;
    (*newSprite)->frames = oldSprite->frames;
    (*newSprite)->frameIndex = (uint32_t *)malloc(sizeof(uint32_t));
    *(*newSprite)->frameIndex = *oldSprite->frameIndex;
}

void ARC_Sprite_SetOpacity(ARC_Sprite *sprite, uint8_t opacity){
    sprite->opacity = opacity;
}

void ARC_Sprite_Render(ARC_Sprite *sprite, ARC_Renderer *renderer, ARC_Rect *renderBounds){
    //TODO: note, this is set here so not all entities in the sheet get opacity set
    SDL_SetTextureAlphaMod((SDL_Texture *)sprite->spritesheet->texture, sprite->opacity); 
    SDL_RenderCopy((SDL_Renderer *)renderer, sprite->spritesheet->texture, (SDL_Rect *)sprite->frames->data + *sprite->frameIndex, (SDL_Rect *)renderBounds);
}

void ARC_Sprite_RenderFlip(ARC_Sprite *sprite, ARC_Renderer *renderer, ARC_Rect *renderBounds, enum ARC_Sprite_Axis axis){
    SDL_RendererFlip flip = SDL_FLIP_NONE;
    if(axis & ARC_SPRITE_AXIS_X){
        flip |= SDL_FLIP_HORIZONTAL;
    }

    if(axis & ARC_SPRITE_AXIS_Y){
        flip |= SDL_FLIP_VERTICAL;
    }

    //TODO: note, this is set here so not all entities in the sheet get opacity set
    SDL_SetTextureAlphaMod((SDL_Texture *)sprite->spritesheet->texture, sprite->opacity); 
    SDL_RenderCopyEx((SDL_Renderer *)renderer, sprite->spritesheet->texture, (SDL_Rect *)sprite->frames->data + *sprite->frameIndex, (SDL_Rect *)renderBounds, 0.0, NULL, flip);
}

void ARC_Sprite_RenderRotated(ARC_Sprite *sprite, ARC_Renderer *renderer, ARC_Rect *renderBounds, ARC_Point *center, double angle){
    //TODO: note, this is set here so not all entities in the sheet get opacity set
    SDL_SetTextureAlphaMod((SDL_Texture *)sprite->spritesheet->texture, sprite->opacity); 
    SDL_RenderCopyEx((SDL_Renderer *)renderer, sprite->spritesheet->texture, (SDL_Rect *)sprite->frames->data + *sprite->frameIndex, (SDL_Rect *)renderBounds, angle, (SDL_Point *)center, SDL_FLIP_NONE);
}

void ARC_Sprite_SetFrameIndex(ARC_Sprite *sprite, uint32_t index){
    if(sprite->frames->size <= index){
        arc_errno = ARC_ERRNO_DATA;
        ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("in ARC_Sprite_SetFrameIndex(sprite, %d); index out of bounds", index);
        return;
    }
    *sprite->frameIndex = index;
}

void ARC_Sprite_IterateFrame(ARC_Sprite *sprite){
    ++*sprite->frameIndex;

    if(*sprite->frameIndex == sprite->frames->size){
        *sprite->frameIndex = 0;
    }
}

uint32_t ARC_Sprite_GetFrameIndex(ARC_Sprite *sprite){
    return *sprite->frameIndex;
}

ARC_Rect *ARC_Sprite_GetBounds(ARC_Sprite *sprite){
    return (ARC_Rect *)sprite->frames->data + *sprite->frameIndex;
}

ARC_Array *ARC_Sprite_GetAllBounds(ARC_Sprite *sprite){
    return sprite->frames;
}