/*
								+--------------------------------+
								|                                |
								|     ***   OpenGL 3.0   ***     |
								|                                |
								|  Copyright © -tHE SWINe- 2009  |
								|                                |
								|          Texture.cpp           |
								|                                |
								+--------------------------------+
*/

/**
 *	@file gl3/Texture.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief OpenGL 3.0 textures
 */

#include "../NewFix.h"
#include "../CallStack.h"
#include "Texture.h"

/*
 *								=== CGLTexture ===
 */

#define Check_InternalFormat_Format(n_internal_format,n_format) do { \
	_ASSERTE(!(n_internal_format == GL_DEPTH_COMPONENT || \
		(n_internal_format >= GL_DEPTH_COMPONENT16 && \
		n_internal_format <= GL_DEPTH_COMPONENT32)) || \
		n_format == GL_DEPTH_COMPONENT); \
	_ASSERTE(!(n_internal_format == GL_DEPTH_STENCIL || \
		n_internal_format == GL_DEPTH24_STENCIL8) || \
		n_format == GL_DEPTH_STENCIL); \
	} while(false)
// if internal format is one of depth formats, then format must
// not be default GL_RGBA, even if no data is specified. same goes
// for packed depth / stencil formats. it's better to assert it,
// than wonder why glTexImage generates errors ...

CGLTexture::CGLTexture(GLenum n_internal_format, GLenum n_target)
	:m_n_id(0), m_n_internal_format(n_internal_format), m_n_target(n_target)
{}

CGLTexture::~CGLTexture()
{
	if(m_n_id)
		glDeleteTextures(1, &m_n_id);
}

bool CGLTexture::b_Status() const
{
	return m_n_id > 0 && glIsTexture(m_n_id);
}

int CGLTexture::n_Max_Size()
{
	int n_size;
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n_size);
	return n_size;
}

float CGLTexture::f_Max_Anisotropy()
{
	float f_result = 1;
	glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &f_result);
	return f_result;
}

GLenum CGLTexture::n_Get_Texture_Wrap_S() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_WRAP_S, (int*)&n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Wrap_T() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_WRAP_T, (int*)&n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Wrap_R() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_WRAP_R, (int*)&n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Min_Filter() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_MIN_FILTER, (int*)&n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Mag_Filter() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_MAG_FILTER, (int*)&n_result);
	return n_result;
}

float CGLTexture::f_Get_Texture_Min_LOD() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_MIN_LOD, &f_result);
	return f_result;
}

float CGLTexture::f_Get_Texture_Max_LOD() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_MAX_LOD, &f_result);
	return f_result;
}

float CGLTexture::f_Get_Texture_LOD_Bias() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_LOD_BIAS, &f_result);
	return f_result;
}

int CGLTexture::n_Get_Texture_Base_Level() const
{
	int n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_BASE_LEVEL, &n_result);
	return n_result;
}

int CGLTexture::n_Get_Texture_Max_Level() const
{
	int n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_MAX_LEVEL, &n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Compare_Mode() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_COMPARE_MODE, (int*)&n_result);
	return n_result;
}

GLenum CGLTexture::n_Get_Texture_Compare_Func() const
{
	GLenum n_result;
	glGetTexParameteriv(m_n_target, GL_TEXTURE_COMPARE_FUNC, (int*)&n_result);
	return n_result;
}

float CGLTexture::f_Get_Texture_Anisotropy() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_MAX_ANISOTROPY, &f_result);
	return f_result;
}

void CGLTexture::Set_Texture_Wrap_S(GLenum n_wrap_mode)
{
	glTexParameteri(m_n_target, GL_TEXTURE_WRAP_S, n_wrap_mode);
}

void CGLTexture::Set_Texture_Wrap_T(GLenum n_wrap_mode)
{
	glTexParameteri(m_n_target, GL_TEXTURE_WRAP_T, n_wrap_mode);
}

void CGLTexture::Set_Texture_Wrap_R(GLenum n_wrap_mode)
{
	glTexParameteri(m_n_target, GL_TEXTURE_WRAP_R, n_wrap_mode);
}

void CGLTexture::Set_Texture_Min_Filter(GLenum n_minify_filter)
{
	glTexParameteri(m_n_target, GL_TEXTURE_MIN_FILTER, n_minify_filter);
}

void CGLTexture::Set_Texture_Mag_Filter(GLenum n_magnify_filter)
{
	glTexParameteri(m_n_target, GL_TEXTURE_MAG_FILTER, n_magnify_filter);
}

/*void CGLTexture::Set_Texture_Border_Color(const float *p_border_color)
{
	glTexParameterfv(m_n_target, GL_TEXTURE_BORDER_COLOR, p_border_color); // deprecated
}

void CGLTexture::Set_Texture_Priority(float f_priority)
{
	glTexParameterf(m_n_target, GL_TEXTURE_PRIORITY, f_priority); // deprecated
}*/

void CGLTexture::Set_Texture_Min_LOD(float f_min_lod)
{
	glTexParameterf(m_n_target, GL_TEXTURE_MIN_LOD, f_min_lod);
}

void CGLTexture::Set_Texture_Max_LOD(float f_max_lod)
{
	glTexParameterf(m_n_target, GL_TEXTURE_MAX_LOD, f_max_lod);
}

void CGLTexture::Set_Texture_LOD_Bias(float f_lod_bias)
{
	glTexParameterf(m_n_target, GL_TEXTURE_LOD_BIAS, f_lod_bias);
}

void CGLTexture::Set_Texture_Base_Level(int n_base_level)
{
	glTexParameteri(m_n_target, GL_TEXTURE_BASE_LEVEL, n_base_level);
}

void CGLTexture::Set_Texture_Max_Level(int n_max_level)
{
	glTexParameteri(m_n_target, GL_TEXTURE_MAX_LEVEL, n_max_level);
}

/*void CGLTexture::Set_Depth_Texture_Mode(GLenum n_depth_mode)
{
	glTexParameteri(m_n_target, GL_DEPTH_TEXTURE_MODE, n_depth_mode); // deprecated
}*/

void CGLTexture::Set_Texture_Compare_Mode(GLenum n_compare_mode)
{
	glTexParameteri(m_n_target, GL_TEXTURE_COMPARE_MODE, n_compare_mode);
}

void CGLTexture::Set_Texture_Compare_Func(GLenum n_compare_func)
{
	glTexParameteri(m_n_target, GL_TEXTURE_COMPARE_FUNC, n_compare_func);
}

/*void CGLTexture::Set_Generate_Mipmap(bool b_generate_mipmap)
{
	glTexParameteri(m_n_target, GL_GENERATE_MIPMAP, (b_generate_mipmap)? GL_TRUE : GL_FALSE); // deprecated
}*/

void CGLTexture::Set_Texture_Anisotropy(float f_anisotropy)
{
	glTexParameterf(m_n_target, GL_TEXTURE_MAX_ANISOTROPY, f_anisotropy);
}

/*
 *								=== ~CGLTexture ===
 */

/*
 *								=== CGLTexture_1D ===
 */

CGLTexture_1D::CGLTexture_1D(int n_width, GLenum n_internal_format,
	bool b_create_mipmaps, GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_1D), m_n_width(n_width)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage1D(m_n_target, 0, m_n_internal_format, m_n_width,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_1D ===
 */

/*
 *								=== CGLTexture_2D ===
 */

CGLTexture_2D::CGLTexture_2D(int n_width, int n_height,
	GLenum n_internal_format, bool b_create_mipmaps,
	GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_2D),
	m_n_width(n_width), m_n_height(n_height)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, m_n_internal_format, m_n_width, m_n_height,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_2D ===
 */

/*
 *								=== CGLTexture_Rect ===
 */

CGLTexture_Rect::CGLTexture_Rect(int n_width, int n_height,
	GLenum n_internal_format, GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_RECTANGLE),
	m_n_width(n_width), m_n_height(n_height)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, m_n_internal_format, m_n_width, m_n_height,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		Set_Texture_Min_Filter(GL_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_Rect ===
 */

/*
 *								=== CGLTexture_3D ===
 */

CGLTexture_3D::CGLTexture_3D(int n_width, int n_height, int n_depth,
	GLenum n_internal_format, bool b_create_mipmaps,
	GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_3D),
	m_n_width(n_width), m_n_height(n_height), m_n_depth(n_depth)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage3D(m_n_target, 0, m_n_internal_format, m_n_width, m_n_height, m_n_depth,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_3D ===
 */

/*
 *								=== CGLTexture_2D_Array ===
 */

CGLTexture_2D_Array::CGLTexture_2D_Array(int n_width, int n_height, int n_layer_num,
	GLenum n_internal_format, bool b_create_mipmaps,
	GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_2D_ARRAY),
	m_n_width(n_width), m_n_height(n_height), m_n_layer_num(n_layer_num)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage3D(m_n_target, 0, m_n_internal_format, m_n_width, m_n_height, m_n_layer_num,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_2D_Array ===
 */

/*
 *								=== CGLTexture_1D_Array ===
 */

CGLTexture_1D_Array::CGLTexture_1D_Array(int n_width, int n_layer_num,
	GLenum n_internal_format, bool b_create_mipmaps,
	GLenum n_format, GLenum n_data_type, const void *p_src_data)
	:CGLTexture(n_internal_format, GL_TEXTURE_2D_ARRAY),
	m_n_width(n_width), m_n_layer_num(n_layer_num)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, m_n_internal_format, m_n_width, m_n_layer_num,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_1D_Array ===
 */

/*
 *								=== CGLTexture_Cube ===
 */

const GLenum CGLTexture_Cube::p_cube_face[6] = {
	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
	GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
	GL_TEXTURE_CUBE_MAP_POSITIVE_Z
};

CGLTexture_Cube::CGLTexture_Cube(int n_width, GLenum n_internal_format,
	bool b_create_mipmaps, bool b_software_mipmaps,
	GLenum n_format, GLenum n_data_type, const void *p_src_data[6])
	:CGLTexture(n_internal_format, GL_TEXTURE_CUBE_MAP), m_n_width(n_width)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	glGenTextures(1, &m_n_id);
	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		for(int i = 0; i < 6; ++ i) {
			glTexImage2D(p_cube_face[i], 0, m_n_internal_format, m_n_width, m_n_width,
				0, n_format, n_data_type, (p_src_data)? p_src_data[i] : NULL);
		}

		if(b_create_mipmaps)
			glGenerateMipmap(m_n_target); // todo - see if this works

		if(!b_create_mipmaps)
			Set_Texture_Min_Filter(GL_LINEAR);
		else
			Set_Texture_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Texture_Mag_Filter(GL_LINEAR);

		if(glGetError() != GL_NO_ERROR) {
			glDeleteTextures(1, &m_n_id);
			m_n_id = 0;
		}
	}
}

/*
 *								=== ~CGLTexture_Cube ===
 */
