#pragma once
#include <glad/glad.h>
#include <string>

#include "../util/stb_image.h"
#include "../util/property.h"
#include "../exceptions/IOException.h"

// Maybe make a base "Texture" class and make a Texture2D and Texture3D child class. look into what would make more sense
class Texture2D
{
public:
	/// <summary>
	/// Defines how the texture should be wrapped if the uv-coordinates are out of bounds.
	/// Default: GL_REPEAT
	/// </summary>
	Property<GLenum> TextureWrapMode;
	/// <summary>
	/// Defines how a texel should be interpolated when zooming out of the texture.
	/// Default: GL_LINEAR
	/// </summary>
	Property<GLenum> MinifyingInterpolation;
	/// <summary>
	/// Defines how a texel should be interpolated when zooming in to the texture.
	/// Default: GL_LINEAR
	/// </summary>
	Property<GLenum> MagnifyingInterpolation;


	// maybe add a parameter for configuring mipmapping
	Texture2D(std::string pathToTexture, GLenum fileFormat = GL_RGB, int desiredColourChannels = 0)
		: TextureWrapMode{GL_REPEAT}, MinifyingInterpolation{GL_LINEAR}, MagnifyingInterpolation{GL_LINEAR}
	{

		unsigned char* image = stbi_load(pathToTexture.c_str(), &width, &height, &nrChannels, desiredColourChannels);
		if (!image)
		{
			stbi_image_free(image);
			throw Nebulix::IOException("ERROR::FILE::IO::LOAD_TEXTURE"); // it would be better to just use a default error image instead and make the error message a warning message
		}
		glGenTextures(1, &textureId);
		glBindTexture(GL_TEXTURE_2D, textureId);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, fileFormat, GL_UNSIGNED_BYTE, image);
		glGenerateMipmap(GL_TEXTURE_2D);
		stbi_image_free(image);
	}
	~Texture2D()
	{
		glDeleteTextures(1, &textureId);
	}

	/// <summary>
	/// Activate and bind this texture to use it. Also sets properties like texture wrapping and interpolation
	/// </summary>
	/// <param name="texture">The texture unit of this texture. For example GL_TEXTURE0 (default)</param>
	void BindTexture(GLenum textureUnit = GL_TEXTURE0)
	{
		// MAybe move these to a seperate, virtual method? And also maybe make this method virtual to allow a child class "Texture3D"
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, TextureWrapMode());
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinifyingInterpolation());
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagnifyingInterpolation());

		glActiveTexture(textureUnit); // before binding texture, activate correct textre Unit (some drivers might show nothing if this is omitted)
		glBindTexture(GL_TEXTURE_2D, textureId);
	}

	int GetWidth() { return width; }
	int GetHeight() { return height; }
	int GetNrChannels() { return nrChannels; }
private:
	GLuint textureId;
	int width, height, nrChannels;
};
