#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),
	spriteAtlas(resourceManager->loadSpriteAtlas(animData->spriteAtlas, animData->frameSize, animData->directional)),
	FPS(animData->FPS),
	currentFrame(0),
	cycles(0),
	isDirectional(animData->directional),
	isPlaying(isDirectional)
{}

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)
{
	curAnim = nullptr;
	for (const auto& anim : animSet)
	{
		anims.try_emplace(anim->type, std::make_unique<Animation>(anim, resourceManager));
		if (anim->type == "idle")
			curAnim = anims[anim->type].get();
	}
	// 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].get();
	isDirectional = curAnim->getDirectional();
}
AnimationSet::AnimationSet(const int& entityid, std::unordered_map<std::string, std::unique_ptr<Animation>> animations) 
: entityid(entityid), anims(std::move(animations))
{
	for (auto& [type, anim] : animations)
	{
		curAnim = anim.get();
		if (type == "idle")
		{
			break;
		}
	}
	isDirectional = curAnim->getDirectional();
}

void AnimationSet::attachEventManager(std::weak_ptr<EventManager> e)
{
	eventManager = e;

	if (auto event = eventManager.lock()) {
		std::weak_ptr<AnimationSet> weakSelf = shared_from_this();

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

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

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

		event->subscribe("OnEntityReload", [weakSelf](std::shared_ptr<Event> e) {
			if (auto self = weakSelf.lock()) {
				auto reloadEvent = std::static_pointer_cast<EntityReloadEvent>(e);
				if (reloadEvent->entityid == self->entityid)
				{
					if (self->anims["reload"] != NULL)
					{
						self->curAnim = self->anims["reload"].get();
						self->curAnim->reset();
					}
				}
			}
		});

		event->subscribe("OnEntityFinishReload", [weakSelf](std::shared_ptr<Event> e) {
			if (auto self = weakSelf.lock()) {
				auto reloadEvent = std::static_pointer_cast<EntityFinishReloadEvent>(e);
				if (reloadEvent->entityid == self->entityid)
				{
					if (self->anims["idle"] != NULL)
						self->curAnim = self->anims["idle"].get();
				}
			}
		});

		event->subscribe("OnEntityFire", [weakSelf](std::shared_ptr<Event> e) {
			if (auto self = weakSelf.lock()) {
				auto fireEvent = std::static_pointer_cast<EntityFireEvent>(e);
				if (fireEvent->entityid == self->entityid)
				{
					if (self->anims["fire"] != NULL)
					{
						self->curAnim = self->anims["fire"].get();
						self->curAnim->reset();
						float newFPS = (1000.f / fireEvent->fireDelay) * 15.f;
						self->curAnim->setFPS(newFPS);
					}
				}
			}
		});

		event->subscribe("OnAnimationFinished", [weakSelf](std::shared_ptr<Event> e) {
			if (auto self = weakSelf.lock()) {
				auto animEvent = std::static_pointer_cast<AnimationFinishedEvent>(e);
				if (animEvent->entityid == self->entityid && animEvent->animType == "fire")
				{
					if (self->anims["idle"] != NULL)
						self->curAnim = self->anims["idle"].get();
				}
			}
		});
	}
}

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 the animation has cycled, we send this event after every cycle
	if ((curAnim->getCycles() - lastCycle) > 0) {
		if (auto event = eventManager.lock()) {
			event->notify(std::make_shared<AnimationFinishedEvent>(entityid, curAnim->getType()));
		}
	}
}

