#include "texture.h"

#include <stb/stb_image.h>
#include <stb/stb_image_write.h>

#include "resource.h"
#include "internal.h"

namespace kek {

static GLuint allocTexture(unsigned char *data, int width, int height, float borderColor[4]) {
	GLuint id;
	glCreateTextures(GL_TEXTURE_2D, 1, &id);
	glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTextureParameterfv(id, GL_TEXTURE_BORDER_COLOR, borderColor);
	glTextureParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTextureParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTextureStorage2D(id, 1, GL_RGB8, width, height);
	glTextureSubImage2D(id, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
	//glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	glGenerateTextureMipmap(id);
	return id;
}

Texture::Texture(std::string texturePath) {
	this->path = texturePath;

	int width, height, nrChannels;
	MemoryBuffer *buf = Resource::loadResource(texturePath);
	if(!buf) {
		std::cout << "Failed to load texture: " << texturePath << std::endl;
		throw std::exception();
	}

	unsigned char *data = stbi_load_from_memory((stbi_uc *) buf->buffer, buf->length, &width, &height, &nrChannels, 0);
	delete buf;

	float borderColor[] = { 1.0f, 0.0f, 1.0f, 1.0f };
	id = allocTexture(data, width, height, borderColor);

	stbi_image_free(data);
}

Texture::Texture(GLuint id) {
	this->id = id;
}

Texture::~Texture() {
	glDeleteTextures(1, &id);
}

void Texture::use(GLenum texture) {
	glActiveTexture(texture);
	glBindTexture(GL_TEXTURE_2D, id);
}

std::shared_ptr<Texture> Texture::load(std::string texturePath) {
	std::shared_ptr<Texture> ret;

	auto iter = kekData.loadedTextures.find(texturePath);
	if(iter != kekData.loadedTextures.end()) ret = iter->second.lock();

	if(!ret) {
		ret = std::make_shared<Texture>(texturePath);
		kekData.loadedTextures.emplace(texturePath, ret);
	}

	return ret;
}

std::shared_ptr<Texture> Texture::generateColor(glm::vec3 color) {
	unsigned char *data = (unsigned char *) malloc(3 * sizeof(unsigned char));
	data[0] = (unsigned char) (color.x * 255);
	data[1] = (unsigned char) (color.y * 255);
	data[2] = (unsigned char) (color.z * 255);
	float borderColor[4] = { color.x, color.y, color.z, 1.0f };
	GLuint tex = allocTexture(data, 1, 1, borderColor);
	free(data);
	return std::make_shared<Texture>(tex);
}

}
