#pragma once

#include <ft2build.h>
#include FT_FREETYPE_H
#include <glm/glm.hpp>

#include <string>
#include <map>

#include "color.h"
#include "resource.h"

namespace kek {

struct Character {
	unsigned int textureX;
	unsigned int textureY;
	glm::ivec2 size;
	glm::ivec2 bearing;
	unsigned int advance;
};

struct CharacterBlock {
	unsigned int textureID;
	std::map<unsigned int, Character> characters;
};

struct TextMetrics {
	int offsetX;
	int offsetY;
	int width;
	int height;

	TextMetrics(int offsetX, int offsetY, unsigned int width, unsigned int height) {
		this->offsetX = offsetX;
		this->offsetY = offsetY;
		this->width = width;
		this->height = height;
	}
};

struct FontMetrics {
	int lineHeight;
	int ascender;
	int descender;

	FontMetrics(int lineHeight, int ascender, int descender) {
		this->lineHeight = lineHeight;
		this->ascender = ascender;
		this->descender = descender;
	}
};

class Font;

struct TextBlock {
	unsigned int characterBlock;
	unsigned int length;

	unsigned int bufferSize;
	unsigned int vao;
	unsigned int vbo;
};

class TextObject {

private:
	Font *font;
	std::string text;

	int offsetX;
	int offsetY;
	int width;
	int height;

public:
	std::map<unsigned int, TextBlock> blocks;

	TextObject(Font *font, std::string text);

	~TextObject();

	TextObject(const TextObject &) = delete;

	TextObject &operator=(const TextObject &) = delete;

	TextObject(TextObject &&other);

	TextObject &operator=(TextObject &&other);

	Font *getFont();

	void setText(std::string text);

	std::string getText();

	TextMetrics getMetrics(int sizePixels);

private:
	void destroy();

	void allocateBuffer(TextBlock *block, int numChars);

	void loadChars();

};

class Font {

private:
	int lineHeight;
	int ascender;
	int descender;

	MemoryBuffer *fileMemory;
	FT_Face face;

	std::map<unsigned int, CharacterBlock> characterBlocks;

public:

	Font(std::string path);

	~Font();

	CharacterBlock getCharacterBlock(unsigned int block);

	TextObject *createText(std::string text);

	FontMetrics getDefaultMetrics();

	FontMetrics getMetrics(int sizePixels);

	// Draws text normally.
	// y specifies the baseline of the drawn text
	void drawText(TextObject *textObject, int x, int y, int sizePixels, Color color);

	// Draws text normally.
	// y specifies the baseline of the drawn text
	void drawText(TextObject *textObject, glm::mat4 projection, int x, int y, int sizePixels, Color color);

	// Draws text, with (x,y) being the top-left corner of the text drawn
	void drawTextFromOrigin(TextObject *textObject, int x, int y, int sizePixels, Color color);

	// Draws text, with (x,y) being the top-left corner of the text drawn
	void drawTextFromOrigin(TextObject *textObject, glm::mat4 projection, int x, int y, int sizePixels, Color color);

	// Draws text centered around (x,y)
	void drawTextCentered(TextObject *textObject, int x, int y, int sizePixels, Color color);

	// Draws text centered around (x,y)
	void drawTextCentered(TextObject *textObject, glm::mat4 projection, int x, int y, int sizePixels, Color color);

private:
	CharacterBlock generateCharacterBlock(unsigned int block);

};

}
