/**
 * $Id: GPC_PolygonMaterial.cpp,v 1.10 2004/06/26 09:15:40 kester Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef WIN32
#include <windows.h>
#endif // WIN32
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif



#include "GPC_PolygonMaterial.h"
#include "MT_Vector3.h"
#include "RAS_IRasterizer.h"
#include "RAS_GLExtensionManager.h"

/* This list includes only data type definitions */
#include "DNA_object_types.h"
#include "DNA_material_types.h"
#include "DNA_image_types.h"
#include "DNA_lamp_types.h"
#include "DNA_group_types.h"
#include "DNA_scene_types.h"
#include "DNA_camera_types.h"
#include "DNA_property_types.h"
#include "DNA_text_types.h"
#include "DNA_sensor_types.h"
#include "DNA_controller_types.h"
#include "DNA_actuator_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"

#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_mesh.h"

#include "MEM_guardedalloc.h"

#include "IMB_imbuf_types.h"
/* end of blender include block */

static Image *fCurpage=0;
static int fCurtile=0, fCurmode=0, fCurTileXRep=0, fCurTileYRep=0;
static short fTexWindx, fTexWindy, fTexWinsx, fTexWinsy;
static int fDoMipMap = 1;
static int fLinearMipMap=1;
static int fAlphamode= -1;

using namespace bgl;
	/* (n&(n-1)) zeros the least significant bit of n */
static int is_pow2(int num) {
	return ((num)&(num-1))==0;
}
static int smaller_pow2(int num) {
	while (!is_pow2(num))
		num= num&(num-1);
	return num;	
}

static void my_make_repbind(Image *ima)
{
	if(ima==0 || ima->ibuf==0) return;

	if(ima->repbind) {
		glDeleteTextures(ima->totbind, (GLuint*)ima->repbind);
		delete (ima->repbind);
		ima->repbind= 0;
	}
	ima->totbind= ima->xrep*ima->yrep;
	if(ima->totbind>1) {
		ima->repbind= (unsigned int *) malloc(sizeof(int)*ima->totbind);
		for (int i=0;i<ima->totbind;i++)
			((int*)ima->repbind)[i] = 0;
	}
}


static int my_set_tpage(TFace *tface)
{	
	static TFace *lasttface= 0;
	Image *ima;
	unsigned int *rect, *bind;
	int tpx, tpy, tilemode, tileXRep,tileYRep;

	/* disable */
	if(tface==0) {
		if(lasttface==0) return 0;
		
		lasttface= 0;
		fCurtile= 0;
		fCurpage= 0;
		if(fCurmode!=0) {
			glMatrixMode(GL_TEXTURE);
			glLoadIdentity();
			glMatrixMode(GL_MODELVIEW);
		}
		fCurmode= 0;
		fCurTileXRep=0;
		fCurTileYRep=0;
		fAlphamode= -1;
		
		glDisable(GL_BLEND);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		return 0;
	}
	lasttface= tface;

	if( fAlphamode != tface->transp) {
		fAlphamode= tface->transp;

		if(fAlphamode) {
			glEnable(GL_BLEND);
			
			if(fAlphamode==TF_ADD) {
				glBlendFunc(GL_ONE, GL_ONE);
			/* 	glBlendEquationEXT(GL_FUNC_ADD_EXT); */
			}
			else if(fAlphamode==TF_ALPHA) {
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			/* 	glBlendEquationEXT(GL_FUNC_ADD_EXT); */
			}
			/* else { */
			/* 	glBlendFunc(GL_ONE, GL_ONE); */
			/* 	glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT); */
			/* } */
		}
		else glDisable(GL_BLEND);
	}

	ima= (struct Image *) tface->tpage;

	/* Enable or disable environment mapping */
	if (ima && (ima->flag & IMA_REFLECT)){

		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
	}
	else{
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
	}

	tilemode= tface->mode & TF_TILES;
	tileXRep = 0;
	tileYRep = 0;
	if (ima)
	{
		tileXRep = ima->xrep;
		tileYRep = ima->yrep;
	}


	if(ima==fCurpage && fCurtile==tface->tile && tilemode==fCurmode && fCurTileXRep==tileXRep && fCurTileYRep == tileYRep) return ima!=0;

	if(tilemode!=fCurmode || fCurTileXRep!=tileXRep || fCurTileYRep != tileYRep)
	{
		glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
		
		if(tilemode && ima!=0)
			glScalef(ima->xrep, ima->yrep, 1.0);

		glMatrixMode(GL_MODELVIEW);
	}

	if(ima==0 || ima->ok==0) {
		glDisable(GL_TEXTURE_2D);
		
		fCurtile= tface->tile;
		fCurpage= 0;
		fCurmode= tilemode;
		fCurTileXRep = tileXRep;
		fCurTileYRep = tileYRep;

		return 0;
	}

	if(ima->ibuf==0) {	
		load_image(ima, IB_rect, "", 0);
		
		if(ima->ibuf==0) {
			ima->ok= 0;

			fCurtile= tface->tile;
			fCurpage= 0;
			fCurmode= tilemode;
			fCurTileXRep = tileXRep;
			fCurTileYRep = tileYRep;
			
			glDisable(GL_TEXTURE_2D);
			return 0;
		}
		
	}

	if(ima->tpageflag & IMA_TWINANIM)	fCurtile= ima->lastframe;
	else fCurtile= tface->tile;

	if(tilemode) {
		
		if(ima->repbind==0) my_make_repbind(ima);
		
		if(fCurtile>=ima->totbind) fCurtile= 0;
		
		/* this happens when you change repeat buttons */
		if(ima->repbind) bind= ima->repbind+fCurtile;
		else bind= &ima->bindcode;
		
		if(*bind==0) {
			
			fTexWindx= ima->ibuf->x/ima->xrep;
			fTexWindy= ima->ibuf->y/ima->yrep;
			
			if(fCurtile>=ima->xrep*ima->yrep) fCurtile= ima->xrep*ima->yrep-1;
	
			fTexWinsy= fCurtile / ima->xrep;
			fTexWinsx= fCurtile - fTexWinsy*ima->xrep;
	
			fTexWinsx*= fTexWindx;
			fTexWinsy*= fTexWindy;
	
			tpx= fTexWindx;
			tpy= fTexWindy;

			rect= ima->ibuf->rect + fTexWinsy*ima->ibuf->x + fTexWinsx;
		}
	}
	else {
		bind= &ima->bindcode;
		
		if(*bind==0) {
			tpx= ima->ibuf->x;
			tpy= ima->ibuf->y;
			rect= ima->ibuf->rect;
		}
	}

	if(*bind==0) {
		int rectw= tpx, recth= tpy;
		unsigned int *tilerect= NULL, *scalerect= NULL;

		/*
		 * Maarten:
		 * According to Ton this code is not needed anymore. It was used only
		 * in really old Blenders.
		 * Reevan:
		 * Actually it is needed for backwards compatibility.  Simpledemo 6 does not display correctly without it.
		 */
#if 1
		 if (tilemode) {
			int y;
				
			tilerect= (unsigned int*)MEM_mallocN(rectw*recth*sizeof(*tilerect), "tilerect");
			for (y=0; y<recth; y++) {
				unsigned int *rectrow= &rect[y*ima->ibuf->x];
				unsigned int *tilerectrow= &tilerect[y*rectw];
					
				memcpy(tilerectrow, rectrow, tpx*sizeof(*rectrow));
			}
				
			rect= tilerect;
		}
#endif
		if (!is_pow2(rectw) || !is_pow2(recth)) {
			rectw= smaller_pow2(rectw);
			recth= smaller_pow2(recth);
			
			scalerect= (unsigned int *)MEM_mallocN(rectw*recth*sizeof(*scalerect), "scalerect");
			gluScaleImage(GL_RGBA, tpx, tpy, GL_UNSIGNED_BYTE, rect, rectw, recth, GL_UNSIGNED_BYTE, scalerect);
			rect= scalerect;
		}

		glGenTextures(1, (GLuint*)bind);

		/*
		if(G.f & G_DEBUG) {
			printf("var1: %s\n", ima->id.name+2);
			printf("var1: %d, var2: %d\n", *bind, tpx);
			printf("var1: %d, var2: %d\n", fCurtile, tilemode);
		}
		*/
		glBindTexture( GL_TEXTURE_2D, *bind);
		
		if (!fDoMipMap)
		{
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		} else
		{
			int minfilter= fLinearMipMap?GL_LINEAR_MIPMAP_LINEAR:GL_LINEAR_MIPMAP_NEAREST;
			
			gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, rectw, recth, GL_RGBA, GL_UNSIGNED_BYTE, rect);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minfilter);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}

		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		if (tilerect)
			MEM_freeN(tilerect);
		if (scalerect)
			MEM_freeN(scalerect);
	}
	else glBindTexture( GL_TEXTURE_2D, *bind);
	
	

	glEnable(GL_TEXTURE_2D);

	fCurpage= ima;
	fCurmode= tilemode;
	fCurTileXRep = tileXRep;
	fCurTileYRep = tileYRep;

	return 1;
}


GPC_PolygonMaterial::GPC_PolygonMaterial(const STR_String& texname, bool ba, const STR_String& matname,
			int tile, int tileXrep, int tileYrep, int mode, bool transparant, bool zsort,
			int lightlayer, bool bIsTriangle, void* clientobject, void* tpage) :
			RAS_IPolyMaterial(texname, ba, matname, tile, tileXrep, tileYrep, mode,
			transparant, zsort, lightlayer, bIsTriangle, clientobject), m_tface((struct TFace*)tpage)
{
	// clear local caching info
	my_set_tpage(0);
}


GPC_PolygonMaterial::~GPC_PolygonMaterial(void)
{
}


void GPC_PolygonMaterial::Activate(RAS_IRasterizer* rasty, TCachingInfo& cachingInfo) const
{
	if (GetCachingInfo() != cachingInfo)
	{
		if (!cachingInfo)
		{
			my_set_tpage(0);
		}
		cachingInfo = GetCachingInfo();

		if ((m_drawingmode & 4)&& (rasty->GetDrawingMode() == RAS_IRasterizer::KX_TEXTURED) )
		{
			update_realtime_texture((struct TFace*) m_tface, rasty->GetTime());
			my_set_tpage(m_tface);
			rasty->EnableTextures(true);
		} else
		{
			my_set_tpage(0);
			rasty->EnableTextures(false);
		}
	
		//TF_TWOSIDE == 512, todo, make this a ketsji enum
		if(m_drawingmode & 512) {
			rasty->SetCullFace(false);
		}
			
		else
		{
			rasty->SetCullFace(true);//glEnable(GL_CULL_FACE);
			//else glDisable(GL_CULL_FACE);
		}
	}
	rasty->SetSpecularity(m_specular[0],m_specular[1],m_specular[2],m_specularity);
	rasty->SetShinyness(m_shininess);
	rasty->SetDiffuse(m_diffuse[0], m_diffuse[1],m_diffuse[2], 1.0);
}


void GPC_PolygonMaterial::SetMipMappingEnabled(bool enabled)
{
	fDoMipMap = enabled ? 1 : 0;
}
