#ifndef _H_SPRITE_H
#define _H_SPRITE_H

#include <SDL_image.h>
#include <glad/glad.h>

#include <glm/glm.hpp>

#include <vector>
#include <unordered_map>
#include <memory>

#include "utility/direction.h"

enum class TileType;
class Texture;
class EventManager;

class Sprite
{
public:
	virtual ~Sprite() {}

	void bind();
	virtual void draw() = 0;
	virtual void play() = 0;
	virtual void idle() = 0;

	virtual std::shared_ptr<Sprite> clone() const = 0;
protected:
	Texture* texture = nullptr;
	unsigned indices[6] = {
		0, 1, 2,
		3, 2, 0
	};
};

class SpriteStatic : public Sprite
{
public:
	SpriteStatic(const char* texturePath);
	~SpriteStatic();

	void draw() override;
	void play() override { /*unused*/ }
	void idle() override { /*unused*/ }

	std::shared_ptr<Sprite> clone() const override {
		return std::make_shared<SpriteStatic>(*this);
	}

private:
	void setupSprite();

	// Vertex Array, Vertex Buffer, Element Buffer
	unsigned VAO, VBO, EBO;

	// simple rectangle. Most scales will be done based on this generic vertex data. 
	float vertices[20] = {
		// vertex		  texturecoords
		0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom left
		1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom right
		1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right
		0.0f, 1.0f, 0.0f, 0.0f, 1.0f  // top left
	};

};

class SpriteAnimated : public Sprite
{
public:
	// let's keep texture grids squares, saves everyone time...
	SpriteAnimated(const char* textureAtlasPath, float frameSize);
	~SpriteAnimated();

	void draw() override;

	void play() override;
	void idle() override;

	std::shared_ptr<Sprite> clone() const override {
		return std::make_shared<SpriteAnimated>(*this);
	}

	bool isPlaying = false;

private:
	void SetupAnimated(float frameSize);

	struct VertexIDs {
		unsigned VAO, VBO;
	};
	unsigned EBO;

	// Vertex array buffer IDs
	std::vector<VertexIDs> ids;

	int currentFrame = 0;
	float FPS = 7.5f;
	Uint32 lastFrameTick = 0;
};

class SpriteDirectionalAnimated : public Sprite
{
public:
	SpriteDirectionalAnimated(const char* textureAtlasPath, float frameSize);
	~SpriteDirectionalAnimated();

	void draw() override;

	void play() override;
	void idle() override;

	std::shared_ptr<Sprite> clone() const override {
		return std::make_shared<SpriteDirectionalAnimated>(*this);
	}

	Direction direction = Direction::Down;
	void setDirection(Direction& dir) { direction = dir; }
	bool isPlaying = false;

private:
	void Setup(float frameSize);

	struct VertexIDs {
		unsigned VAO, VBO;
	};
	unsigned EBO;

	std::unordered_map<Direction, VertexIDs> idleIDs;
	std::unordered_map<Direction, std::vector<VertexIDs>> walkingIDs;

	int currentFrame = 0;
	float FPS = 7.5f;
	Uint32 lastFrameTick = 0;
};

#endif // _H_SPRITE_H