#include "graphics/animation.h"
#include "graphics/sprite.h"
#include "utility/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/events.h"

Animation::Animation(const std::shared_ptr<AnimationData>& animData, ResourceManager* resourceManager) :
	animName(animData->name),
	animType(animData->type),
	FPS(animData->FPS),
	currentFrame(0),
	cycles(0),
	isDirectional(animData->directional),
	isPlaying(isDirectional),
	spriteAtlas(resourceManager->loadSpriteAtlas(animData->spriteAtlas, animData->frameSize, animData->directional))
{}

void Animation::bind()
{
	spriteAtlas->bind();
}

void Animation::draw()
{
	singleDraw();
}

void Animation::draw(Direction dir)
{
	directionalDraw(dir);
}

void Animation::singleDraw()
{
	if (isPlaying)
	{
		frameTick();
		auto frame = spriteAtlas->frame(currentFrame);
		spriteAtlas->bindFrame(&frame);
		spriteAtlas->draw();
	}
	else
	{
		auto frame = spriteAtlas->frame(0);
		spriteAtlas->bindFrame(&frame);
		spriteAtlas->draw();
	}
}

void Animation::directionalDraw(Direction dir)
{
	if (isPlaying)
	{
		frameTick();
	}
	auto frame = spriteAtlas->frame(currentFrame, dir);
	spriteAtlas->bindFrame(&frame);
	spriteAtlas->draw();
}

void Animation::frameTick()
{
	Uint32 currentTime = SDL_GetTicks();
	elapsedTime = currentTime - lastFrameTick;
	if (elapsedTime >= 1000.0f / FPS)
	{
		if (++currentFrame > spriteAtlas->size() - 1)
		{
			currentFrame = 0;
			cycles += 1;
		}
		lastFrameTick = currentTime;
	}
}

AnimationSet::AnimationSet(const int& entityid) : entityid(entityid), isDirectional(false) {};
AnimationSet::AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<std::shared_ptr<AnimationData>> animSet) : entityid(entityid)
{
	for (const auto& anim : animSet)
	{
		anims.try_emplace(anim->type, std::make_shared<Animation>(anim, resourceManager));
		if (anim->type == "idle")
			curAnim = anims[anim->type];
	}
	// if we don't have an idle animation, we are just gonna set the current animation to the top of the set
	if (curAnim == nullptr)
		curAnim = anims[animSet[0]->type];
	isDirectional = curAnim->getDirectional();
}
AnimationSet::AnimationSet(const int& entityid, const std::unordered_map<std::string, std::shared_ptr<Animation>>& animations) : entityid(entityid), anims(animations)
{
	for (const auto& [type, anim] : animations)
	{
		curAnim = anim;
		if (type == "idle")
		{
			break;
		}
	}
	isDirectional = curAnim->getDirectional();
}

void AnimationSet::attachEventManager(const std::shared_ptr<EventManager>& e)
{
	eventManager = e;

	eventManager->subscribe("OnDirectionChange", [this](std::shared_ptr<Event> e) {
		auto directionEvent = std::static_pointer_cast<DirectionChangeEvent>(e);
		if (directionEvent->entityid == entityid)
			setFacingDir(directionEvent->direction);
	});

	eventManager->subscribe("OnEntityMove", [this](std::shared_ptr<Event> e) {
		auto moveEvent = std::static_pointer_cast<EntityMoveEvent>(e);
		if (moveEvent->entityid == entityid)
		{
			if (isDirectional)
			{
				if (anims["move"] != NULL)
					curAnim = anims["move"];
			}
			else
				play();
		}
	});

	eventManager->subscribe("OnEntityStop", [this](std::shared_ptr<Event> e) {
		auto stopEvent = std::static_pointer_cast<EntityStopEvent>(e);
		if (stopEvent->entityid == entityid)
		{
			if (isDirectional)
			{
				if (anims["idle"] != NULL)
					curAnim = anims["idle"];
			}
			else
				stop();
		}
	});

	eventManager->subscribe("OnEntityReload", [this](std::shared_ptr<Event> e) {
		auto reloadEvent = std::static_pointer_cast<EntityReloadEvent>(e);
		if (reloadEvent->entityid == entityid)
		{
			if (anims["reload"] != NULL)
				curAnim = anims["reload"];
		}
	});

	eventManager->subscribe("OnEntityFinishReload", [this](std::shared_ptr<Event> e) {
		auto reloadEvent = std::static_pointer_cast<EntityFinishReloadEvent>(e);
		if (reloadEvent->entityid == entityid)
		{
			if (anims["idle"] != NULL)
				curAnim = anims["idle"];
		}
	});
}

void AnimationSet::bind()
{
	curAnim->bind();
}

void AnimationSet::draw()
{
	int lastCycle = curAnim->getCycles();
	if (curAnim->getDirectional())
		curAnim->draw(facing);
	else
		curAnim->draw();

	// Sending animation events
	if (eventManager)
	{
		// If the animation has cycled, we send this event after every cycle
		if ((curAnim->getCycles() - lastCycle) > 0)
			eventManager->notify(std::make_shared<AnimationFinishedEvent>(entityid, curAnim->getType()));
	}
}

