#include "animation_sprite.h"
#include "debug.h"
#include "program.h"

struct AnimationSprite {
	Spritesheet* sheet;
	Sprite* sprite_target;

	AnimationSpriteLoopMode loop_mode;

	float frame_interval;
	float start_time;
};

AnimationSprite* animation_sprite_new(Sprite* target_sprite, Spritesheet* sheet, float framerate, AnimationSpriteLoopMode loop_mode) {
	AnimationSprite* self = malloc(sizeof(AnimationSprite));
	ASSERT_RETURN(self != NULL, NULL, "Failed to allocate memory for AnimationSprite");
	*self = (AnimationSprite){
		.sheet = sheet,
		.frame_interval = 1.0f / framerate,
		.loop_mode = loop_mode,
		.start_time = game_time(),
		.sprite_target = target_sprite
	};

	return self;
}

void animation_sprite_destroy(AnimationSprite* self) {
	spritesheet_destroy(self->sheet);
	free(self);
}

void animation_sprite_play_from(AnimationSprite* self, float normalized_time) {
	self->start_time = game_time() - normalized_time * animation_sprite_get_length(self);
}

void animation_sprite_draw(AnimationSprite* self, Transform* transform) {
    const size_t frame_count = spritesheet_get_tile_count(self->sheet);
	const float time = game_time() - self->start_time;
    
    size_t frame = (size_t)(time / self->frame_interval);
    
    switch(self->loop_mode) {
    case LoopMode_Hide:
        if(frame >= frame_count)
            return;
        else
            break;
    case LoopMode_Loop:
        frame %= frame_count;
        break;
    case LoopMode_PingPong:
        frame %= frame_count * 2;
        if(frame >= frame_count)
            frame = frame_count - (frame - frame_count);
        return;
    case LoopMode_Stop:
        if(frame >= frame_count)
            frame = frame_count - 1;
    }
    
    sprite_set_spritesheet(self->sprite_target, self->sheet);
	sprite_set_tile(self->sprite_target, frame);
	sprite_draw(self->sprite_target, *transform);
}

float animation_sprite_get_length(AnimationSprite* self) {
	return (float)spritesheet_get_tile_count(self->sheet) * self->frame_interval;
}

void animation_sprite_set_framerate(AnimationSprite* self, float framerate) {
	self->frame_interval = 1.0f / framerate;
}

float animation_sprite_get_framerate(const AnimationSprite* self) {
	return 1.0f / self->frame_interval;
}

Sprite* animation_sprite_get_sprite(AnimationSprite* self) {
    return self->sprite_target;
}

float animation_sprite_get_time(AnimationSprite* self) {
    return game_time() - self->start_time;
}

float animation_sprite_get_time_normalized(AnimationSprite* self) {
    return animation_sprite_get_time(self) / animation_sprite_get_length(self);
}
