/*********************************************************\
         __      __           __  __
        / /     / / _____    / / / / ______   __     __
       / /     / / /___  \  / / / / / ___  | / /    / /
      / /_____/ / _____| / / / / / / /__/ / / /    / /
     / ______  / / ___  / / / / / / _____/ / /    / /
    / /     / / / /__/ / / / / / / |____  / |____/ /
   /_/     /_/ |______/ /_/ /_/  \_____/  \_____  /
                                         ______/ /
            High Level Game Framework   /_______/

  ------------------------------------------------------

  Copyright (c) 2009 - Rodrigo Braz Monteiro.
  This file is subject to the terms of license.txt.

\*********************************************************/

#include "draw.h"
#include "texture.h"

namespace Halley {

	void DrawImage(Texture *tex, Vector2i pos, Vector2i size, int cutX)
	{
		DrawMaskedImage(tex, NULL, pos, size, cutX);
	}

	void DrawMaskedImage(Texture *tex1, Texture *tex2, Vector2i pos, Vector2i size, int cutX)
	{
		// Bind main texture
		SetTextureUnit(0);
		float buffer[8], buffer2[8];
		tex1->Bind();
		tex1->GetTexCoords(buffer);
		if (cutX != 0) {
			float val = (float)(cutX+0.5f) / tex1->GetW();
			buffer[0] += val;
			buffer[6] += val;
		}
		glTexCoordPointer(2, GL_FLOAT, 0, buffer);

		// Bind mask texture
		if (tex2) {
			SetNumberOfTextureUnits(2);
			SetTextureUnit(1);
			tex2->Bind();
			tex2->GetTexCoords(buffer2);
			if (cutX != 0) {
				float val = (float)(cutX+0.5f) / tex2->GetW();
				buffer2[0] += val;
				buffer2[6] += val;
			}
			glTexCoordPointer(2, GL_FLOAT, 0, buffer2);
			SetTextureUnit(0);
		} else {
			SetNumberOfTextureUnits(1);
		}

		// Set vertices
		int x1 = pos.x+cutX;
		int x2 = pos.x+size.x;
		int y1 = pos.y;
		int y2 = pos.y+size.y;
		GLint vertices[] = { x1, y1, x2, y1, x2, y2, x1, y2 };

		// Draw
		SetBlendType(tex1->GetAlphaType());
		glVertexPointer(2, GL_INT, 0, vertices);
		glDrawArrays(GL_QUADS, 0, 4);
	}

	//////////////////

	class GLInternals {
	public:
		GLInternals()
		{
			for (int i=0; i<8; i++) {
				curTex[i] = 0;
			}
			numUnits = 1;
			curTexUnit = 0;
			curBlend = Texture::ALPHA_OPAQUE;
		}

		int curTexUnit;
		int numUnits;
		int curTex[8];
		Texture::AlphaType curBlend;
	};
	static GLInternals glState;

	////////////////////

	void SetBlendType(Texture::AlphaType type)
	{
		const Texture::AlphaType curType = glState.curBlend;
		if (curType != type) {
			bool hasBlend = curType == Texture::ALPHA_BLEND || curType == Texture::ALPHA_ADD;
			bool needsBlend = type == Texture::ALPHA_BLEND || type == Texture::ALPHA_ADD;
			bool hasTest = curType == Texture::ALPHA_TEST;
			bool needsTest = type == Texture::ALPHA_TEST;

			// Disable current
			if (hasBlend && !needsBlend) glDisable(GL_BLEND);
			else if (!hasBlend && needsBlend) glEnable(GL_BLEND);
			if (hasTest && !needsTest) glDisable(GL_ALPHA_TEST);
			else if (!hasTest && needsTest) {
				glEnable(GL_ALPHA_TEST);
				glAlphaFunc(GL_GREATER, 0.1f);
			}

			if (needsBlend) {
				if (type == Texture::ALPHA_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				else glBlendFunc(GL_ONE, GL_ONE);
			}

			// Update current
			glState.curBlend = type;
		}
	}

	void SetTextureUnit(int n)
	{
		assert (n >= 0);
		assert (n < 8);

		if (glState.curTexUnit != n) {
			glActiveTexture(GL_TEXTURE0 + n);
			glClientActiveTexture(GL_TEXTURE0 + n);
			glState.curTexUnit = n;
		}
	}

	void BindTexture(int id)
	{
		assert (id >= 0);

		if (id != glState.curTex[glState.curTexUnit]) {
			glState.curTex[glState.curTexUnit] = id;
			glBindTexture(GL_TEXTURE_2D, id);
		}
	}

	void SetNumberOfTextureUnits(int n)
	{
		assert (n >= 1);
		assert (n < 8);

		// Enable units
		if (n > glState.numUnits) {
			for (int i=glState.numUnits; i<n; i++) {
				SetTextureUnit(i);
				glEnable(GL_TEXTURE_2D);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			}
		}

		// Disable units
		else {
			for (int i=n; i<glState.numUnits; i++) {
				SetTextureUnit(i);
				glDisable(GL_TEXTURE_2D);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			}
		}

		glState.numUnits = n;
	}

}
