#include "stdafx.h"
#include "basic.h"
#include "Textures.h"
#include "Eroc.h"
#include "time.h"
#include "OGLState.h"
#include <stdlib.h>

#ifdef import_globals
#undef import_globals
#endif
#include "globals.h"

// Framerate
int theNrFramesThisSec = 0;
int theFPSLastSec = 0;
time_t theLastTime = 0;

#define PI 3.141592654

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


/////////////////////////////////////////////////////////////////////////////
int gNrAnims = 0;
Animation gAnims[256];

void	LoadAnims()
{
	FILE* AnimList = fopen("AnimList.txt", "r");
	if (! AnimList )
		return;

	long int Duration;
	char Token[256];
	while (1 == fscanf(AnimList, " %s", Token ))
	{		
		if (strcmp(Token, "BEGIN") == 0)
		{		
			Animation& Anim = gAnims[gNrAnims];
			while(2 == fscanf(AnimList, " %s %ld", Token, &Duration ))
			{
				// Load the texture so it is cached
				CreateTextureFromFile(Token, false);
				Anim.mFrames[Anim.mNrFrames++] = new Frame(Token, Duration);
			}

			gNrAnims++;

			if (strcmp(Token, "END") != 0)
				break;			
		}
	}
	fclose(AnimList);
}

Animation*	GetAnim(int inNr)
{
	Animation* Result = 0;
	if (inNr >= 0 && inNr < gNrAnims)
		Result = &(gAnims[inNr]);
	return Result;
}

void	FreeAnims()
{
	for (int i = 0; i < gNrAnims; i++)
	{
		for ( int j = 0; j < gAnims[i].mNrFrames; j++)
			delete gAnims[i].mFrames[j];
	}
	gNrAnims = 0;
}

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

EROC_API BOOL Clear ( void ) 
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	return true;
}

EROC_API BOOL BackgroundColor( signed long inRed, signed long inGreen, signed long inBlue)
{
	OGLState::PushBGColor(255, inRed, inGreen, inBlue);
	glClear( GL_COLOR_BUFFER_BIT);
	OGLState::PopBGColor();
	return true;
}


EROC_API BOOL Background ( signed long inImageNr ) 
{
	return BackgroundFade(inImageNr, 256);
}


EROC_API BOOL BackgroundFade ( signed long inImageNr, signed long inOpacity ) 
{
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	OGLState::PushTexture2D(true);

	Texture* Tex = GetTexture(inImageNr);
	OGLState::PushTextureUsed(Tex->mOpenGLTex);

	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	glPushMatrix ( );
	
	float ScaleX = Tex->mScaleX;
	float ScaleY = Tex->mScaleY;
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( 1, 1, 1, inOpacity/256.0f );
		glTexCoord2f(0, ScaleY);		glVertex3f (-gAspectX,-1.0, -1.0);
		glTexCoord2f(ScaleX, ScaleY);	glVertex3f ( gAspectX,-1.0, -1.0);
		glTexCoord2f(ScaleX, 0);		glVertex3f ( gAspectX, 1.0, -1.0);
		glTexCoord2f(0, 0);				glVertex3f (-gAspectX, 1.0, -1.0);
	glEnd ( );	
	glPopMatrix ( );

	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();
	OGLState::PopBlendFunc();	
	OGLState::PopBlend();						
	return true;
}

EROC_API BOOL FastBackground ( signed long inImageNr, signed long inPosX, signed long inPosY,
							   signed long inSizeX, signed long inSizeY )
{
	OGLState::PushTexture2D(true);
	Texture* Tex = GetTexture(inImageNr);		
	OGLState::PushTextureUsed(Tex->mOpenGLTex);
	
	glPushMatrix ( );
	
	float ScaleX = Tex->mScaleX;
	float ScaleY = Tex->mScaleY;

	float TexX1 = ScaleX*inPosX/1000.0f;
	float TexY1 = ScaleY*inPosY/1000.0f;
	float TexX2 = TexX1 + ScaleX*inSizeX/1000.0f;
	float TexY2 = TexY1 + ScaleY*inSizeY/1000.0f;
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor3f ( 1, 1, 1 );
		glTexCoord2f(TexX1,TexY2);		glVertex3f ( -gAspectX, -1.0, -1.0);
		glTexCoord2f(TexX2,TexY2);		glVertex3f (  gAspectX, -1.0, -1.0);
		glTexCoord2f(TexX2,TexY1);		glVertex3f (  gAspectX,  1.0, -1.0);
		glTexCoord2f(TexX1,TexY1);		glVertex3f ( -gAspectX,  1.0, -1.0);
	glEnd ( );

	glPopMatrix ( );

	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	return true;
}

// Scales & draws an image with alpha (if enabled), no translation
BOOL TexImage( const Texture* inTex, signed long inScale, signed long inOpacity )
{
	OGLState::PushTexture2D(true);
	OGLState::PushTextureUsed(inTex->mOpenGLTex);
	
	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	float TCenterX = inTex->mScaleX/2;
	float TCenterY = inTex->mScaleY/2;
	float THalfX =  inTex->mScaleX/2;
	float THalfY =  inTex->mScaleY/2;
	
	float HalfX = inScale *inTex->mSizeX/(gVScreenY*1000);
	float HalfY = inScale *inTex->mSizeY/(gVScreenY*1000);
	
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( 1, 1, 1, inOpacity/256.0f );
		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,-HalfY, -1.0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f ( HalfX,-HalfY, -1.0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f ( HalfX, HalfY, -1.0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX, HalfY, -1.0 );
	glEnd ( );
		
	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	return true;
}

// Scales & draws an image without or without alpha, but with the colors faded, no translation
BOOL TexImageFaded( const Texture* inTex, signed long inScale, signed long inOpacity )
{
	OGLState::PushTexture2D(true);
	OGLState::PushTextureUsed(inTex->mOpenGLTex);
	
	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	float TCenterX = inTex->mScaleX/2;
	float TCenterY = inTex->mScaleY/2;
	float THalfX =  inTex->mScaleX/2;
	float THalfY =  inTex->mScaleY/2;
	
	float HalfX = inScale *inTex->mSizeX/(gVScreenY*1000);
	float HalfY = inScale *inTex->mSizeY/(gVScreenY*1000);
	
	glBegin ( GL_QUADS );
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glColor4f ( inOpacity/256.0f, inOpacity/256.0f, inOpacity/256.0f, inOpacity/256.0f );
		glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,-HalfY, -1.0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f ( HalfX,-HalfY, -1.0 );
		glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f ( HalfX, HalfY, -1.0 );
		glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX, HalfY, -1.0 );
	glEnd ( );
		
	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	return true;
}

// Doesn't set blending!
BOOL TexImagePlace( const Texture* inTex, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity ) 
{
	glPushMatrix ( );
	
	float CenterX = 2*inPosX/gVScreenX - 1;
	float CenterY = 1-2*inPosY/gVScreenY ;

	glTranslatef(CenterX, CenterY, 0);
	
	TexImage(inTex, inScale, inOpacity);

	glPopMatrix ( );

	return true;
}

// This one sets blending!
EROC_API BOOL Image( signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity )
{
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	bool theResult = TexImagePlace( GetTexture(inImageNr),inPosX, inPosY, inScale, inOpacity );
	OGLState::PopBlendFunc();	
	OGLState::PopBlend();
	return theResult;
}

EROC_API BOOL Animate( signed long inAnimNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity, signed long inTime ) 
{
	Animation* Anim = GetAnim(inAnimNr);
	if ( Anim )
	{
		int Frame = -1;
		while (inTime >= 0)
		{
			if ( ++Frame >= Anim->mNrFrames)
				Frame = 0;

			inTime -= Anim->mFrames[Frame]->mDuration;
		}
		// "load" cached texture
		int ImageNr = CreateTextureFromFile(Anim->mFrames[Frame]->mName, false);

		Image( ImageNr, inPosX, inPosY, inScale, inOpacity);
	}
	return true;
}

EROC_API BOOL PrintNr ( signed long inNr, signed long inPosX, signed long inPosY )
{
	int theX = inPosX;
	int theY = (int)(gVScreenY - inPosY);

	OGLState::PushBlend(false);

	char String[256];
	sprintf (String, "%li", inNr);
	glPrint(theX, theY,String, 0);

	OGLState::PopBlend();
	return true;
}

EROC_API BOOL ShowFPS ( signed long inPosX, signed long inPosY )
{
	OGLState::PushBlend(false);
	// Calculate FPS, should move to BeginNewFrame
	time_t theTime = time(0);
	if (theTime != theLastTime)
	{
		theLastTime = theTime;
		theFPSLastSec = theNrFramesThisSec;
		theNrFramesThisSec = 0;
	}
	theNrFramesThisSec++;

	int theX = inPosX;
	int theY = (int)(gVScreenY - inPosY);
	char theFPS[10];
	sprintf(theFPS, "%d", theFPSLastSec);
	glPrint(theX, theY, theFPS ,0);	// Print GL Text To The Screen

	OGLState::PopBlend();
	return true;
}

// Nr waves = x/1000 * up & down wave
// Size : 1000 = size image
// Offset: 1000 = 1 up/down sine wave
EROC_API BOOL Wave( signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inOpacity,
					 signed long inWavesX, signed long inWavesY, signed long inSizeX, signed long inSizeY,
					 signed long inOffsetX,signed long inOffsetY)
{
	Texture* Tex = GetTexture(inImageNr);

	int SectionsX = 1;
	int SectionsY = 1;
	if (inWavesX)
	{
		// for now, take about 15 sections per wave, regardless of magnitude
		SectionsX = 15*inWavesX;
	}
	if (inWavesY)
	{
		// for now, take about 15 sections per wave, regardless of magnitude
		SectionsY = 15*inWavesY;
	}

	// sanity check
	if (inOpacity < 0)
		inOpacity = 0;
	else if (inOpacity > 256)
		inOpacity = 256;

	GLboolean TextureEnable = glIsEnabled(GL_TEXTURE_2D);
	if ( !TextureEnable)
		glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping

	glPushMatrix ( );

	float TCenterX = Tex->mScaleX/2;
	float TCenterY = Tex->mScaleY/2;
	float THalfX =  Tex->mScaleX/2;
	float THalfY =  Tex->mScaleY/2;
	
	float CenterX = 2*inPosX/gVScreenX - 1;
	float CenterY = 1-2*inPosY/gVScreenY ;
	float HalfX = inScale *Tex->mSizeX/(gVScreenY*1000);
	float HalfY = inScale *Tex->mSizeY/(gVScreenY*1000);

	glBindTexture(GL_TEXTURE_2D, Tex->mOpenGLTex);
	glNormal3f( 0.0f, 0.0f, 1.0f);
	glColor4f ( 1, 1, 1, inOpacity/256.0f );

	float StartX = CenterX - HalfX;
	float StartY = CenterY - HalfY;
	float IncX = 2*HalfX/SectionsX;
	float IncY = 2*HalfY/SectionsY;

	float TStartX = TCenterX - THalfX;
	float TStartY = TCenterY + THalfY;
	float TIncX = 2*THalfX/SectionsX;
	float TIncY = 2*THalfY/SectionsY;

	for (int j = 0; j < SectionsY; j++)
	{

		// X waves are based on y-coordinates and vice-versa
		float WaveX = HalfX*inSizeY/1000.0 * sin( 2.0*PI * inOffsetY/1000.0 +  (2.0*PI * j * inWavesX) / SectionsX );
		float WaveXP = HalfX*inSizeY/1000.0 * sin( 2.0*PI * inOffsetY/1000.0 + (2.0*PI * (j+1) * inWavesX) / SectionsX );

		for (int i = 0; i < SectionsX; i++)
		{
			float WaveY = HalfY*inSizeX/1000.0 * sin( 2.0*PI*inOffsetX/1000.0 + (2.0*PI * i * inWavesY) / SectionsY );
			float WaveYP = HalfY*inSizeX/1000.0 * sin( 2.0*PI*inOffsetX/1000.0 + (2.0*PI * (i+1) * inWavesY) / SectionsY );

			glBegin(GL_QUADS);
				glTexCoord2f(TStartX + i*TIncX, TStartY - j*TIncY);			glVertex3f (StartX + i*IncX +WaveX, StartY + j*IncY +WaveY, -1.0 );
				glTexCoord2f(TStartX + (i+1)*TIncX, TStartY - j*TIncY);		glVertex3f (StartX + (i+1)*IncX +WaveX, StartY + j*IncY +WaveYP, -1.0 );
				glTexCoord2f(TStartX + (i+1)*TIncX, TStartY - (j+1)*TIncY);	glVertex3f (StartX + (i+1)*IncX +WaveXP, StartY + (j+1)*IncY +WaveYP, -1.0 );
				glTexCoord2f(TStartX + i*TIncX, TStartY - (j+1)*TIncY);		glVertex3f (StartX + i*IncX +WaveXP, StartY + (j+1)*IncY +WaveY, -1.0 );
			glEnd();
		}
	}

	glPopMatrix ( );

	if (! TextureEnable)
		glDisable(GL_TEXTURE_2D);

	return true;
}

// 
void	
GreyTexture(unsigned int* inLFB, int inX, int inY)
{
	for (unsigned int y = 0; y < inY; y++)
		for (unsigned int x = 0; x < inX; x++)
		{		
			// Monochrome:  red		= 30% = 76/255;
			//				green	= 59% = 151/255
			//				blue	= 11% = 28/255

			unsigned int theColor = *(inLFB + y*inX + x);
			unsigned int theRed   = 76 * ((theColor >> 16 ) & 0xFF);
			unsigned int theGreen = 151 * ((theColor >> 8 ) & 0xFF);
			unsigned int theBlue  = 28 * ((theColor >> 0 ) & 0xFF);

			*(inLFB + y*inX + x) = 0x010101 * ((theRed + theGreen + theBlue) >> 8);
		}
}

int Clamp(int Value, int Upper)
{
	if (Value <=0)
		return 0;

	if (Value >= Upper)
		return Upper;

	return (Value + Upper) % Upper;
	
}

void
DeformTextureBlurHor(unsigned int* inLFB, int inX, int inY, int inAmount)
{
	unsigned int* theTempLFB = new unsigned int[inX*inY];

	for (int y = 0; y < inY; y++)
	{
		// Start cumulative total		
		unsigned int theTotalA = 0, theTotalR = 0, theTotalG = 0, theTotalB = 0;

		for (int i = - inAmount; i <= inAmount; i++) 
		{
			unsigned int* theCurPixel = inLFB + (y * inX) + Clamp(i,inX-1 );
			theTotalA += ((*theCurPixel) >> 24) & 0xFF;
			theTotalR += ((*theCurPixel) >> 16) & 0xFF;
			theTotalG += ((*theCurPixel) >> 8) & 0xFF;
			theTotalB += (*theCurPixel) & 0xFF;
		}
		
		for (int x = 0; x < inX; x++)
		{
			// Draw pixel
			unsigned int theA = (theTotalA*2 + 1) / (2 + (inAmount << 2));
			unsigned int theR = (theTotalR*2 + 1) / (2 + (inAmount << 2));
			unsigned int theG = (theTotalG*2 + 1) / (2 + (inAmount << 2));
			unsigned int theB = (theTotalB*2 + 1) / (2 + (inAmount << 2));
			*(theTempLFB + y * inX + x) = (theA << 24) + (theR << 16) + (theG << 8) + theB;

			// Update Total
			unsigned int* theNewPixel = inLFB + (y * inX) +  Clamp(x + inAmount + 1, inX-1);
			theTotalA += ((*theNewPixel) >> 24) & 0xFF;
			theTotalR += ((*theNewPixel) >> 16) & 0xFF;
			theTotalG += ((*theNewPixel) >> 8) & 0xFF;
			theTotalB += (*theNewPixel) & 0xFF;

			unsigned int* theOldPixel = inLFB + (y * inX) + Clamp(x - inAmount, inX-1);
			theTotalA -= ((*theOldPixel) >> 24) & 0xFF;
			theTotalR -= ((*theOldPixel) >> 16) & 0xFF;
			theTotalG -= ((*theOldPixel) >> 8) & 0xFF;
			theTotalB -= (*theOldPixel) & 0xFF;
		}	
	}
	for (int i = 0; i < inX*inY; i++)
		*(inLFB+i) = *(theTempLFB+i);
    
    delete[] theTempLFB;
}

void
DeformTextureBlurVer(unsigned int* inLFB, int inX, int inY, int inAmount)
{
	unsigned int* theTempLFB = new unsigned int[inX*inY];

	for (int x = 0; x < inX; x++)
	{
		// Start cumulative total	
		unsigned int theTotalA = 0, theTotalR = 0, theTotalG = 0, theTotalB = 0;

		for (int i = - inAmount; i <= inAmount; i++)
		{	
			unsigned int* theCurPixel = inLFB + (Clamp( i, inY-1) * inX) + x;
			theTotalA += ((*theCurPixel) >> 24) & 0xFF;
			theTotalR += ((*theCurPixel) >> 16) & 0xFF;
			theTotalG += ((*theCurPixel) >> 8) & 0xFF;
			theTotalB += (*theCurPixel) & 0xFF;			
		}
		
		for (int y = 0; y < inY; y++)
		{
			// Draw pixel
			unsigned int theA = (theTotalA*2 + 1) / (2 + (inAmount << 2));
			unsigned int theR = (theTotalR*2 + 1) / (2 + (inAmount << 2));
			unsigned int theG = (theTotalG*2 + 1) / (2 + (inAmount << 2));
			unsigned int theB = (theTotalB*2 + 1) / (2 + (inAmount << 2));
			*(theTempLFB + y * inX + x) = (theA << 24) + (theR << 16) + (theG << 8) + theB;

			// Update Total
			unsigned int* theNewPixel = inLFB + x + Clamp(y + inAmount + 1,inY-1) * inX;
			theTotalA += ((*theNewPixel) >> 24) & 0xFF;
			theTotalR += ((*theNewPixel) >> 16) & 0xFF;
			theTotalG += ((*theNewPixel) >> 8) & 0xFF;
			theTotalB += (*theNewPixel) & 0xFF;

			unsigned int* theOldPixel = inLFB + x + Clamp(y - inAmount, inY-1) * inX;
			theTotalA -= ((*theOldPixel) >> 24) & 0xFF;
			theTotalR -= ((*theOldPixel) >> 16) & 0xFF;
			theTotalG -= ((*theOldPixel) >> 8) & 0xFF;
			theTotalB -= (*theOldPixel) & 0xFF;
		}
	}
	for (int i = 0; i < inX*inY; i++)
		*(inLFB+i) = *(theTempLFB+i);
    
    delete[] theTempLFB;
}

// Add color to dest, saturated
void SaturatedAdd(unsigned int& inDest, unsigned int inColor)
{
	for (int i= 0; i < 4; i++)
	{
		unsigned int Component = ((inDest >> (i*8)) & 0xFF) +  ((inColor >> (i*8)) & 0xFF);
		if (Component > 255)
			Component = 255;
		inDest &= ~(0xFF << (i*8));
		inDest += Component << (i*8);
	}
}

// Make dest the maximum of color and dest 
void MaximumAdd(unsigned int& inDest, unsigned int inColor)
{
	for (int i= 0; i < 4; i++)
	{
		unsigned int Component = ((inColor >> (i*8)) & 0xFF);
		if (Component > ((inDest >> (i*8)) & 0xFF))
		{
			inDest &= ~(0xFF << (i*8));
			inDest += Component << (i*8);
		}
	}
}

void ExponentialColor(unsigned int& inColor)
{
	for (int i = 0; i <4; i++)
	{
		unsigned int Component = (inColor >> (i*8)) & 0xFF;
		Component = Component * Component >> 8;
		inColor &= ~(0xFF << (i*8));
		inColor += Component << (i*8);
	}
}


// Cutof: intensity below wich colors are cut of. Above it will be amplified
// ramp: How fast intensity changes. 256 = not at all, higher = faster
// Random: amount of noise   
void ModifyColor(unsigned int& inColor, int inCutoff, int inRamp, int inRandom)
{
	int Noise = rand() % inRandom;
	for (int i = 0; i <4; i++)
	{
		int Component = (inColor >> (i*8)) & 0xFF;
		Component =(((Component - inCutoff) * inRamp) >> 8);
		Component += Noise;
		if (Component < 0)
			Component = 0;
		else if (Component > 255)
			Component = 255;
		
		inColor &= ~(0xFF << (i*8));
		inColor += Component << (i*8);
	}
}

void RampLFB(unsigned int* inLFB, int inLevel, int inRamp, int inSize)
{
	unsigned char* thePixel = (unsigned char *)inLFB;
	for (int i = inSize*4; i ; i--)
	{
		int Component = *thePixel;
		Component = inLevel + (((Component - inLevel) * inRamp) >> 8);
		if (Component < 0)
			Component = 0;
		else if (Component > 255)
			Component = 255;
		
		*(thePixel++) = Component;
	}

}

//
// lines: segments, color = background color of midpoint
// intensity: determines how much 
// rest: alpha = blocks of RasterGap Size 
//
//
void CreateRaster(unsigned int* inLFB, int inSizeX, int inSizeY,
				  unsigned int* inBackGround, unsigned int* inAlphaLayer )
{
	const int RasterGap = 5;
	const int SegmentLength = 20;
	const int MinSegmentLength = 10;
	int i;

	// See ModifyColor
	int Cutoff = 128;
	int Ramp = 256+64;
	int Noise = 50;
	// chance the grid moves a pixel around. Higher = less chance
	int Rigidity = 16;
	// Amount of lines without secondary line. Higher = thin grid
	int DoubleLine = 3;

	// Clear
	for (i = 0; i < inSizeX*inSizeY; i++)
		inLFB[i] = inAlphaLayer[i];

	// Horizontal lines
	for (int y = rand() % RasterGap; y < inSizeY; y += RasterGap)
	{
		
		int StartX = 0;
		int Length = MinSegmentLength + rand() % SegmentLength;

		while (StartX < inSizeX )
		{
			if (StartX + Length > inSizeX)
				Length = inSizeX - StartX;

			// get the background color of the midde of the segment
			unsigned int Color = inBackGround[y*inSizeX + StartX + Length/2];
			ModifyColor(Color, Cutoff, Ramp, Noise);

			unsigned int Color2 = (Color >> 1) & 0x7F7F7F7F;
			for (int j = 0; j < Length; j++)
				MaximumAdd(inLFB[y*inSizeX + StartX + j], Color);

			// Secondary line
			if ((rand() % DoubleLine) && (Length > 4))
			{
				int Skip = rand() % (Length >> 1 );
				int SecLength = (Length >> 1) + rand() % ((Length>>1) - Skip);
				
				int SecY = y + (rand() % 2) * 2 - 1;
				if (SecY < 0)
					SecY = 0;
				else if (SecY >= inSizeY)
					SecY = inSizeY - 1;	

				for (int j = 0; j < SecLength; j++)
					MaximumAdd(inLFB[SecY*inSizeX + StartX +Skip + j], Color2);
			}

			StartX += Length;
			Length = MinSegmentLength + rand() % SegmentLength;

			//Chaos
			if (rand() % Rigidity == 0)
			{
				// Y +- 1
				y += (rand() % 2) * 2 - 1;
				if (y < 0)
					y = 0;
				else if (y >= inSizeY)
					y = inSizeY - 1;
				StartX -= Length;
				if (StartX < 0)
					StartX = 0;
				Length = MinSegmentLength + rand() % SegmentLength;
			}
		}
	}

	//Vertical lines
	for (int x = rand() % RasterGap; x < inSizeX; x += RasterGap)
	{	
		int StartY = 0;
		int Length = Length = MinSegmentLength + rand() % SegmentLength;
		
		while (StartY < inSizeY )
		{
			if (StartY + Length > inSizeY)
				Length = inSizeY - StartY;

			// get the background color of the midde of the segment
			unsigned int  Color = inBackGround[(StartY+Length/2)*inSizeX + x];
			ModifyColor(Color, Cutoff, Ramp, Noise);

			unsigned int Color2 = (Color >> 2) & 0x3F3F3F3F;
			for (int j = 0; j < Length; j++)
				MaximumAdd(inLFB[(StartY+j)*inSizeX + x], Color);
		
			// Secondary line
			if ((rand() % DoubleLine) && (Length > 4))
			{
				int Skip = rand() % (Length >> 1 );
				int SecLength = (Length >> 1) + rand() % ((Length>>1) - Skip);
				
				int SecX = x + (rand() % 2) * 2 - 1;
				if (SecX < 0)
					SecX = 0;
				else if (SecX >= inSizeX)
					SecX = inSizeX - 1;	

				for (int j = 0; j < SecLength; j++)
					MaximumAdd(inLFB[(StartY+j+Skip)*inSizeX + SecX], Color2);
			}

			StartY += Length;
			Length = Length = MinSegmentLength + rand() % SegmentLength;

			//Chaos
			if (rand() % Rigidity == 0)
			{
				// X +- 1
				x += (rand() % 2) * 2 - 1;
				if (x < 0)
					x = 0;
				else if (x >= inSizeX)
					x = inSizeX - 1;
				StartY -= Length;
				if (StartY < 0)
					StartY = 0;
				Length = MinSegmentLength + rand() % SegmentLength;
			}
		}
	}
}

EROC_API BOOL Rasterize(signed long inImageNr, signed long inPosX, signed long inPosY,
					  signed long inScale, signed long inOpacity )
{
	Texture* Tex = GetTexture(inImageNr);
	static int Raster = 0;
    static int AlphaLayer = 0;

	// bright layer one: merged with lines
	static unsigned int* theBrightAlphaLayerLFB = 0;
	//Dark layer: merged with background
	static unsigned int* theDarkAlphaLayerLFB = 0;

	// First create the alpha layer
	if (AlphaLayer == 0)
	{
		int i;
		SDL_Surface* Surface = GetSDLSurface(inImageNr);
		unsigned int* theSurfaceLFB = (unsigned int*)Surface->pixels;
		
		theBrightAlphaLayerLFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		for (i = 0; i < Tex->mSizeX*Tex->mSizeY; i++)
		{
			
			
			if ( theSurfaceLFB[i] & 0xFFFFFF )
				 theBrightAlphaLayerLFB[i] = 0xFF000000;
			else theBrightAlphaLayerLFB[i] = 0x00000000;
		}

		theDarkAlphaLayerLFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		for (i = 0; i < Tex->mSizeX*Tex->mSizeY; i++)
			theDarkAlphaLayerLFB[i] = theBrightAlphaLayerLFB[i];

		DeformTextureBlurHor(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		DeformTextureBlurVer(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		RampLFB(theBrightAlphaLayerLFB, 256,256*2, Tex->mSizeX*Tex->mSizeY);

		DeformTextureBlurHor(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		DeformTextureBlurVer(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 125);
		RampLFB(theDarkAlphaLayerLFB, 256, 256*5, Tex->mSizeX*Tex->mSizeY);

		DeformTextureBlurHor(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 5);
		DeformTextureBlurVer(theDarkAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, 5);

		AlphaLayer = CreateTextureFromMem(theBrightAlphaLayerLFB, Tex->mSizeX, Tex->mSizeY, false);
	}

	// Create new raster
	
	if (Raster == 0)
	{
		SDL_Surface* theBackGround = GetSDLSurface(inImageNr);
		unsigned int* LFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
		CreateRaster(LFB, Tex->mSizeX,Tex->mSizeY, (unsigned int*)theBackGround->pixels, theDarkAlphaLayerLFB);
		Raster = CreateTextureFromMem(LFB, Tex->mSizeX, Tex->mSizeY, false);	
		delete[] LFB;
	}
	else
	{
		static int FrameNr = 0;
		FrameNr++;
		FrameNr %= 5;
		if (FrameNr == 0)
		{
			SDL_Surface* theBackGround = GetSDLSurface(inImageNr);
			unsigned int* LFB = new unsigned int[Tex->mSizeX*Tex->mSizeY];
			CreateRaster(LFB, Tex->mSizeX,Tex->mSizeY, (unsigned int*)theBackGround->pixels,
								theDarkAlphaLayerLFB);

			ReplaceTextureFromMem(Raster, LFB, Tex->mSizeX, Tex->mSizeY, false);
			delete[] LFB;
		}
	}
	
	// Draw fuzzy alpha layer
	OGLState::PushColorMask(true, false, false, false);
	Image(AlphaLayer, inPosX, inPosY, inScale, inOpacity);
	OGLState::PopColorMask();

	// Now draw raster

	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA);
	TexImagePlace(GetTexture(Raster), inPosX, inPosY, inScale, inOpacity);
	OGLState::PopBlendFunc();
	OGLState::PopBlend();

	return true;
}

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

int gCurrentCBlur = -1;
Texture gCBlurLowPlane, gCBlurHighPlane; // the caches of the bitplanes.

void MakeCircularBlurTextures(int inImageNr)
{
	if (inImageNr != gCurrentCBlur)
	{
		SDL_Surface* theSurface = GetSDLSurface(inImageNr);

		gCurrentCBlur = inImageNr;
		LoadGLTextureBitPlane(theSurface, 0, gCBlurLowPlane );
		LoadGLTextureBitPlane(theSurface, 1, gCBlurHighPlane );
	}
}

/* quick and dirty algorithm:
  Conceptually we use two textures, the low pie and the high pie.
  The low pie accumulates 16 rotated low-end bitplanes, 
  high accumulates 16 * the 4 high-end bits (shifted 4 downwards).
  One pie on the screen is made by high + (low/16)
  If more than 16 degrees are required, 2 pies of each are used etc.

  Todo:
  - deftig opkuisen (release p-buffer vertraagt teveel?)
  - geen 512*512 texture gebruiken als niet nodig
    -> niet diameter gebruiken: slechts 16 graden ipv 45!

*/
EROC_API BOOL CircularBlur(signed long inImageNr, signed long inPosX, signed long inPosY,
					 signed long inScale, signed long inDegrees, signed long inBrightness )
{	

	if (inDegrees <= 0)
		return true;

	MakeCircularBlurTextures(inImageNr); // make sure the cache is in place

	// sanity check
	if (inDegrees < 1)
		inDegrees = 1;
	else if (inDegrees > 360)
		inDegrees = 360;

	// find nearest multiple of 16 (at least 1 image per degree)
	int theNrPies = (inDegrees + 15) / 16;
	int theNrImages = theNrPies * 16;
	float theAnglePerPie = (float)inDegrees / theNrPies;
	float theAnglePerImage = (float)inDegrees / theNrImages;
	float theOpacityPerImage = (256.0 / theNrImages) * (inBrightness / 1000.0);

	Texture* inTex = GetTexture(inImageNr);
	float TCenterX = inTex->mScaleX/2;
	float TCenterY = inTex->mScaleY/2;
	float THalfX =  inTex->mScaleX/2;
	float THalfY =  inTex->mScaleY/2;

	// get the diagonal of the texture, to find the correct size for the PBuffe
	int theDiameter = 2*int( sqrt( (inTex->mSizeX/2.0)*(inTex->mSizeX/2.0) +
								(inTex->mSizeY/2.0)*(inTex->mSizeY/2.0) ) + 0.9999 );

	int thePBufferSize = 1;
	while (thePBufferSize < theDiameter)
		thePBufferSize *= 2;
	
	float HalfX = inScale *inTex->mSizeX/(thePBufferSize*1000.0);
	float HalfY = inScale *inTex->mSizeY/(thePBufferSize*1000.0);

	CreatePBuffer(thePBufferSize, thePBufferSize);

	StartRenderToTexture();

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

	glClearColor(0.0, 0.0, 0, 1.0);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	
	// create the low-pie texture

	OGLState::PushTexture2D(true);
	OGLState::PushTextureUsed(gCBlurLowPlane.mOpenGLTex);	
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_ONE, GL_ONE);

	glPushMatrix ( );
		
	glNormal3f( 0.0f, 0.0f, 1.0f);
	glColor4f ( 1, 1, 1, 1 );

	int i;
	for (i = 0; i < 16; i++)
	{
		glBegin ( GL_QUADS );
			glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, -0.50 );
			glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, -0.50 );
			glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, -0.50 );
			glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, -0.50 );
		glEnd ( );
		
		glRotatef ( theAnglePerImage, 0, 0, 1.0f ); // next layer a bit more rotated
	}	
	glPopMatrix ( );

	const Texture * theTexture = EndRenderToTexture();

	// Draw low-pie textures

	glPushMatrix();
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_ONE, GL_ONE);

	float CenterX = 2.0*inPosX/(gVScreenX*10) - 1;
	float CenterY = 1.0-2.0*inPosY/(gVScreenY*10) ;
	glTranslatef(CenterX, CenterY, 0);

	glRotatef( theAnglePerPie*theNrPies/2.0, 0, 0, 1.0f ); // rotate a bit to the other side first
	for (i = 0; i < theNrPies; i++)
	{		
		TexImageFaded( theTexture, inScale, theOpacityPerImage );
		glRotatef( -theAnglePerPie, 0, 0, 1.0f ); // next pie a lot more rotated		
	}
	
	OGLState::PopBlendFunc();
	OGLState::PopBlend();
	glPopMatrix();


	// create the high-pie texture

	StartRenderToTexture();
	
	// uncomment to see dimention of pbuffer
	//glClearColor(1.0, 0.0, 0, 1.0);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	OGLState::SetTextureUsed(gCBlurHighPlane.mOpenGLTex);
	glPushMatrix ( );	
		
	glNormal3f( 0.0f, 0.0f, 1.0f);
	glColor4f ( 1, 1, 1, 1 );

	for (i = 0; i < 16; i++)
	{
		glBegin ( GL_QUADS );		
			glTexCoord2f(TCenterX -THalfX, TCenterY-THalfY);	glVertex3f (-HalfX,-HalfY, -0.50 );
			glTexCoord2f(TCenterX +THalfX, TCenterY-THalfY);	glVertex3f (+HalfX,-HalfY, -0.50 );
			glTexCoord2f(TCenterX +THalfX, TCenterY+THalfY);	glVertex3f (+HalfX,+HalfY, -0.50 );
			glTexCoord2f(TCenterX -THalfX, TCenterY+THalfY);	glVertex3f (-HalfX,+HalfY, -0.50 );
		glEnd ( );
		
		glRotatef ( theAnglePerImage, 0, 0, 1.0f ); // next layer a bit more rotated
	}	

	OGLState::PopBlendFunc();
	OGLState::PopBlend();
	glPopMatrix ( );
	OGLState::PopTextureUsed();
	OGLState::PopTexture2D();

	theTexture = EndRenderToTexture();

	// Draw high-pie textures

	glPushMatrix();
	OGLState::PushBlend(true);
	OGLState::PushBlendFunc(GL_ONE, GL_ONE);
	
	glTranslatef(CenterX, CenterY, 0);

	glRotatef( theAnglePerPie*theNrPies/2.0, 0, 0, 1.0f ); // rotate a bit to the other side first
	for (i = 0; i < theNrPies; i++)
	{		
		TexImageFaded( theTexture, inScale, theOpacityPerImage*16 );
		glRotatef( -theAnglePerPie, 0, 0, 1.0f ); // next pie a lot more rotated		
	}
	
	OGLState::PopBlendFunc();
	OGLState::PopBlend();
	glPopMatrix();

	return true;
}



