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

/**
 *	@file gl4/Texture.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief OpenGL 4.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_STENCIL_INDEX || \
		(n_internal_format >= GL_STENCIL_INDEX1 && \
		n_internal_format <= GL_STENCIL_INDEX16)) || \
		n_format == GL_STENCIL_INDEX); \
	_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),
#ifdef __GL_TEXTURE_STORE_INTERNAL_FORMAT
	m_n_internal_format(n_internal_format),
#endif // __GL_TEXTURE_STORE_INTERNAL_FORMAT
	m_n_target(n_target)
{
	glGenTextures(1, &m_n_id);
}

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_EXT, &f_result);
	return f_result;
}

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

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

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

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

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

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

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

/*float CGLTexture::f_Get_LOD_Bias() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_LOD_BIAS, &f_result);
	return f_result;
}*/ // deprecated

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

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

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

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

float CGLTexture::f_Get_Anisotropy() const
{
	float f_result;
	glGetTexParameterfv(m_n_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, &f_result);
	return f_result;
}

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

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

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

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

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

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

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

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

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

/*void CGLTexture::Set_LOD_Bias(float f_lod_bias)
{
	glTexParameterf(m_n_target, GL_TEXTURE_LOD_BIAS, f_lod_bias);
}*/ // deprecated

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

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

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

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

void CGLTexture::Set_Compare_Func(GLenum n_compare_func) const
{
	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_Anisotropy(float f_anisotropy) const
{
	glTexParameterf(m_n_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 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_1D_Base(n_internal_format, GL_TEXTURE_1D), m_n_width(n_width)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage1D(m_n_target, 0, n_internal_format, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_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_2D_Base(n_internal_format, GL_TEXTURE_2D),
	m_n_width(n_width), m_n_height(n_height)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, n_internal_format, n_width, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_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_2D_Base(n_internal_format, GL_TEXTURE_RECTANGLE),
	m_n_width(n_width), m_n_height(n_height)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, n_internal_format, n_width, n_height,
			0, n_format, n_data_type, (p_src_data)? p_src_data : NULL);

		Set_Min_Filter(GL_LINEAR);
		Set_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_3D_Base(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);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage3D(m_n_target, 0, n_internal_format, n_width, n_height, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_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_3D_Base(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);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage3D(m_n_target, 0, n_internal_format, n_width, n_height, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_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_2D_Base(n_internal_format, GL_TEXTURE_1D_ARRAY),
	m_n_width(n_width), m_n_layer_num(n_layer_num)
{
	Check_InternalFormat_Format(n_internal_format, n_format);

	if(m_n_id > 0) {
		glBindTexture(m_n_target, m_n_id);

		glTexImage2D(m_n_target, 0, n_internal_format, n_width, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_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);

	
	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, n_internal_format, n_width, 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_Min_Filter(GL_LINEAR);
		else
			Set_Min_Filter(GL_LINEAR_MIPMAP_LINEAR);
		Set_Mag_Filter(GL_LINEAR);

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

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