/*  $Id: opengl.cpp,v 1.22 2004/04/20 19:40:52 sphair Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This library is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "Display/display_precomp.h"
#include "API/GL/opengl.h"
#include "API/GL/opengl_wrap.h"

#include <GL/gl.h>
#include <map>

#ifndef WIN32
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h>
#endif

CL_ProcAddress *CL_OpenGL::get_proc_address(const std::string& function_name)
{
#ifdef WIN32
	return (void (*)())wglGetProcAddress(function_name.c_str());
#else
#  ifdef HAVE_GLX_GETPROCADDRESSARB
	return glXGetProcAddressARB((GLubyte*)function_name.c_str());
#  else
	return glXGetProcAddress((GLubyte*)function_name.c_str());
#  endif
#endif
}

CL_GLFunctions *CL_OpenGL::functions = 0;

bool CL_OpenGL::from_opengl_pixelformat(CLenum format, CLenum type, CL_PixelFormat &pf)
{
	int bits_per_component;
	if (type == CL_UNSIGNED_BYTE || type == CL_BYTE)
		bits_per_component = 8;
	else if (type == CL_SHORT || type == CL_UNSIGNED_SHORT)
		bits_per_component = 16;
	else if (type == CL_INT || type == CL_UNSIGNED_INT || type == CL_FLOAT)
		bits_per_component = 32;
	else
		return false;

	if (format == CL_COLOR_INDEX)
	{
		pf.set_type(pixelformat_index);
		pf.set_depth(bits_per_component);
	}
	else if (format == CL_RED)
	{
		pf.set_depth(bits_per_component);
		pf.set_red_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
	}
	else if (format == CL_GREEN)
	{
		pf.set_depth(bits_per_component);
		pf.set_green_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
	}
	else if (format == CL_BLUE)
	{
		pf.set_depth(bits_per_component);
		pf.set_blue_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
	}
	else if (format == CL_ALPHA)
	{
		pf.set_depth(bits_per_component);
		pf.set_alpha_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
	}
	else if (format == CL_RGB)
	{
		if (bits_per_component * 3 > 32)
			return false;
		pf.set_depth(bits_per_component * 3);
		pf.set_red_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
		pf.set_green_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component));
		pf.set_blue_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 2));
	}
	else if (format == CL_RGBA)
	{
		if (bits_per_component * 4 > 32)
			return false;
		pf.set_depth(bits_per_component * 4);
		pf.set_red_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
		pf.set_green_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component));
		pf.set_blue_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 2));
		pf.set_alpha_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 3));
	}
	else if (format == CL_BGR)
	{
		if (bits_per_component * 3 > 32)
			return false;
		pf.set_depth(bits_per_component * 3);
		pf.set_blue_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
		pf.set_green_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component));
		pf.set_red_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 2));
	}
	else if (format == CL_BGRA)
	{
		if (bits_per_component * 4 > 32)
			return false;
		pf.set_depth(bits_per_component * 4);
		pf.set_blue_mask(CL_PixelFormat::get_bitmask(bits_per_component, 0));
		pf.set_green_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component));
		pf.set_red_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 2));
		pf.set_alpha_mask(CL_PixelFormat::get_bitmask(bits_per_component, bits_per_component * 3));
	}
	else
		return false;

	return true;
}

bool CL_OpenGL::to_opengl_pixelformat(const CL_PixelFormat &pf, CLenum &format, CLenum &type)
{
	// indexed modes and colorkey requires special conversion to alpha and
	// therefore no OpenGL mode has a direct conversion format
	if (pf.has_colorkey() || pf.get_type() == pixelformat_index)
		return false;

	if (pf.get_type() == pixelformat_index)
	{
		format = CL_COLOR_INDEX;
		if (pf.get_depth() == 8)
			type = CL_UNSIGNED_BYTE;
		else if (pf.get_depth() == 16)
			type = CL_UNSIGNED_SHORT;
		else if (pf.get_depth() == 32)
			type = CL_UNSIGNED_INT;
		else
			return false;
	}
	else if (pf.get_type() == pixelformat_rgba)
	{
		const unsigned int rm = pf.get_red_mask(),
			gm = pf.get_green_mask(),
			bm = pf.get_blue_mask(),
			am = pf.get_alpha_mask();

		const int rms = pf.get_mask_shift(rm),
			gms = pf.get_mask_shift(gm),
			bms = pf.get_mask_shift(bm),
			ams = pf.get_mask_shift(am);

		const int rmb = pf.get_mask_bits(rm),
			gmb = pf.get_mask_bits(gm),
			bmb = pf.get_mask_bits(bm),
			amb = pf.get_mask_bits(am);

		typedef std::map<int, CLenum> BitsToTypeMap;
		static BitsToTypeMap bits_to_type;
		if (bits_to_type.empty()) {
			// initialize it only once
			bits_to_type[8] = CL_UNSIGNED_BYTE;
			bits_to_type[16] = CL_UNSIGNED_SHORT;
			bits_to_type[32] = CL_UNSIGNED_INT;
		}

		if (rm && gm && bm && am)
		{
			// the bit number must be 8, 16 or 32 for component
			// they all must have the same number of bits
			if ((!(rmb == 8 || rmb == 16 || rmb == 32)) ||
				(rmb != gmb || rmb != bmb || rms != amb))
				return false;

			const int bits = rmb; // they all have the same bits

			if (pf.get_depth() != bits * 4)
				return false;

			type = bits_to_type[bits];

			if (rms == 0*bits && gms == 1*bits && bms == 2*bits && ams == 3*bits)
				format = CL_RGBA;
			else if (ams == 0*bits && bms == 1*bits && gms == 2*bits && rms == 3*bits)
				format = CL_ABGR;
			else
				return false;
		}
		else if (rm && gm && bm && !am)
		{
			// the bit number must be 8, 16 or 32 for component
			// they all must have the same number of bits
			if ((!(rmb == 8 || rmb == 16 || rmb == 32)) ||
				(rmb != gmb || rmb != bmb))
				return false;

			const int bits = rmb; // they all have the same bits

			if (pf.get_depth() != bits * 3)
				return false;

			type = bits_to_type[bits];

			if (rms == 0*bits && gms == 1*bits && bms == 2*bits)
				format = CL_RGB;
			else if (bms == 0*bits && gms == 1*bits && rms == 2*bits)
				format = CL_BGR;
			else
				return false;
		}
		else if (rm && !gm && !bm && !am)
		{
			format = CL_RED;
			if (rms != 0 || !bits_to_type.count(rmb))
				return false;
			if (pf.get_depth() != rmb)
				return false;
			type = bits_to_type[rmb];
		}
		else if (!rm && gm && !bm && !am)
		{
			format = CL_GREEN;
			if (gms != 0 || !bits_to_type.count(gmb))
				return false;
			if (pf.get_depth() != gmb)
				return false;
			type = bits_to_type[gmb];
		}
		else if (!rm && !gm && bm && !am)
		{
			format = CL_BLUE;
			if (bms != 0 || !bits_to_type.count(bmb))
				return false;
			if (pf.get_depth() != bmb)
				return false;
			type = bits_to_type[bmb];
		}
		else if (!rm && !gm && !bm && am)
		{
			format = CL_ALPHA;
			if (ams != 0 || !bits_to_type.count(amb))
				return false;
			if (pf.get_depth() != amb)
				return false;
			type = bits_to_type[amb];
		}
		else
			return false;
	}
	else
		return false;

	return true;
}
