#ifndef	_2DEFFECT_H
#define _2DEFFECT_H

#define X2D		256
#define Y2D		256

class Effect2D
{
public:;
	long flag;
	void *videoBuffer;
	ULONG startTime, time;
	class textu *maps;

	Effect2D();
	void Blur();
	void Foyer();
	void Blit( long );
	void RadialBlurHard(float,float,float,float,float);
	void RadialBlurFX( int, int, float );
	void drawBuffer(float);
	void drawBufferAdd();	// draw buffer in additive

//	Tunnel effect
	int  *TAngle, *TDepth;
	void Init_Tunnel( int );
	void DrawTunnel( int addx, int addy, long text0);

//	flower effect
	int *Flower;
	void CalculateFlower();
	void writeFlower( long text );

//	Raytrace tunnel
	void DrawFDObject( long textu );
	void writeRayPrim();
	float alpha;           
	float cam_rotx,cam_roty ,cam_rotz;
//	------------------

	void clearBuffer();
	void Init_Textures();

	void updateTime();
}videoEffect;

Effect2D::Effect2D()
{
	alpha = 0;
	cam_rotx = 0;
	cam_roty = 0;
	cam_rotz = 0;
}
void Effect2D::updateTime()
{
	time = MyGetTickCount() - startTime;
}

void Effect2D::drawBufferAdd()
{
	glBlendFunc(GL_ONE, GL_ONE);
	drawBuffer(1.0f);
	glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
}

void Effect2D::drawBuffer(float alpha)
{
	initProjFor2D();

	glBindTexture(GL_TEXTURE_2D, GLtexture[videoGLnum]);
	glTexSubImage2D (GL_TEXTURE_2D, 0, 0,0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, videoBuffer);
	glLoadIdentity();		// current matrix is MODEL_MATRIX
	glColor4f( 1, 1, 1, alpha);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glDepthMask( false );
	glBegin(GL_QUADS);

	glTexCoord2f( 0, 1);
	glVertex3f(-1, 1,-1);
	glTexCoord2f( 1, 1);
	glVertex3f( 1, 1,-1);
	glTexCoord2f( 1, 0);
	glVertex3f( 1,-1,-1);
	glTexCoord2f( 0, 0);
	glVertex3f(-1,-1,-1);

	glEnd();
	glDepthMask( true );
	glDisable(GL_TEXTURE_2D);

	initProjFor3D();
}

void Effect2D::Blit( long picture )
{
	for ( int i=0; i<256*256; i++ )
	((long *)videoBuffer)[i] = ((long *)maps[picture].data)[i];
}

void Effect2D::clearBuffer()
{
	for( int m = 0;m<(256*256);m++)	((long*)videoBuffer)[m] = 0;
}
void Effect2D::Init_Textures()
{
	maps = new textu[4];
	bool itsOK = false;

	itsOK = maps[0].loadIMG(0,"D:\\gozervrac\\Dimension3\\tex000.jpg");
	itsOK = maps[1].loadIMG(0,"D:\\gozervrac\\Dimension3\\tex004.jpg");
	itsOK = maps[2].loadIMG(0,"D:\\gozervrac\\Dimension3\\tex005.jpg");
	itsOK = maps[3].loadIMG(0,"D:\\gozervrac\\Dimension3\\synrj.jpg");

	if( itsOK ) quitMessage("Effect2D::Init_Textures()"," can't find a texture");
}

void Effect2D::Init_Tunnel( int flat )
{
	int x,y,cx,cy;

	TAngle  = new int[X2D*Y2D];
	TDepth  = new int[X2D*Y2D];
                  
	cx = X2D >> 1;
	cy = Y2D >> 1;

	for ( y=0; y!=Y2D; y++ )
	{       
		for ( x=0; x!=X2D; x++)
		{   
			TAngle[x+y*X2D] = (int)(atan2( cy-y, cx-x ) * 256 / PI );

			if (flat)
			{   
				TDepth[x+y*X2D] = (int)(sqrt( (x-cx)*(x-cx)+(y-cy)*(y-cy) ));
			}
			else
				TDepth[x+y*X2D] = (int)(256*8/sqrt( (x-cx)*(x-cx)+(y-cy)*(y-cy) ));
		}
	}   
}

void Effect2D::DrawTunnel( int addx, int addy, long text0)
{
	int x,y,tx1,ty1;			// boucle, coord dans les textures, pixel final
	ULONG *dst_dat, *map1_dat,final;  // pointeur sur les maps

	dst_dat  = (ULONG*)videoBuffer;        // Destination
	map1_dat = (ULONG*)maps[text0].data;      // Tex 1
                       
	for ( y=0; y!=Y2D; y++ ) // on boucle
	{                          
		for ( x=0; x!=X2D; x++)
		{              
			// on prends le point de "Tex 1" a afficher selon des tables precalculées dans Init_Tunnel();
			tx1 = (TAngle[x+y*X2D]+addx)&255;
			ty1 = (TDepth[x+y*X2D]+addy)&255;

			// on fait une transparence 50% - 50% des 2 pixels
			final = map1_dat[tx1+ty1*X2D];

			// pis on place le pixel final dans le buffer a la position x,y;
			dst_dat[x+y*X2D] = final;
		}
	}
}



void Effect2D::Blur()
{
	int x,y;
	UCHAR *p = (uchar*)videoBuffer;
	char r,g,b;
	long offset;

	for ( x=1; x<255; x++ )
	{
		for ( y=1; y<255; y++ )
		{
			offset = x + y * 256;   

			r  = (( p[((offset-1)<<2)]     + p[((offset+1)<<2)]      + p[((offset-256)<<2)]     + p[((offset+256)<<2)] )    >>  1) - p[(offset<<2)     ];
			g = (( p[((offset-1)<<2)+1] + p[((offset+1)<<2)+1] + p[((offset-256)<<2)+1] + p[((offset+256)<<2)+1]) >> 1) - p[(offset<<2) +1];
			b = (( p[((offset-1)<<2)+2] + p[((offset+1)<<2)+2] + p[((offset-256)<<2)+2] + p[((offset+256)<<2)+2]) >> 1) - p[(offset<<2) + 2];

			if ( r<0 ) r  = 0;
			if ( g<0 ) g = 0;
			if ( b<0 ) b = 0;

			p[(offset<<2)       ] = r;
			p[(offset<<2) + 1] = g;
			p[(offset<<2) + 2] = b;
		}
	}
}

float FXtime = 0;

void Effect2D::Foyer()
{
	UCHAR *p = (uchar *)videoBuffer;
//	int a = (int)( 128 + 120 * cos( time / 32 ));
//	int b = (int)( 128 + 120 * sin( time  / 16 ));

//	int a = (int)(128+ 128.0f*(float)(rand()-RAND_MAX/2)/16384);
//	int b = (int)(128+ 128.0f*(float)(rand()-RAND_MAX/2)/16384);
	int a = (int)(256.0*(float)rand()/(float)RAND_MAX);
	int b = (int)(256.0*(float)rand()/(float)RAND_MAX);

	int cr = (int)( 127 + 128 * sin( FXtime  / 32 ));
	int cg = (int)( 127 + 128 * sin( FXtime  / 16 ));
	int cb = (int)( 127 + 128 * sin( FXtime  / 24 ));

	if( a <= 1 ) a = 2;
	if( a >= 255) a = 254;
	if( b <= 1) b = 2;
	if( b >= 255) b = 254;
//	a = 128;
//	b = 128;
    long offset = a + ( b * 256);
//    if( offset < 256 ) offset = 256;
//    if( offset >= ((256*256)-256) ) offset = ((256*256)-257);

//	for( int s=0;s<256*256*4;s++) p[s] = rand()>>8;

	p[(offset<<2)    ] = cr;
	p[(offset<<2) + 1] = cg;
	p[(offset<<2) + 2] = cb;     

	p[((offset-1)<<2)    ] = cr;
	p[((offset-1)<<2) + 1] = cg;
	p[((offset-1)<<2) + 2] = cb;

	p[((offset+1)<<2)    ] = cr;
	p[((offset+1)<<2) + 1] = cg;
	p[((offset+1)<<2) + 2] = cb;

	p[((offset-256)<<2)    ] = cr;
	p[((offset-256)<<2) + 1] = cg;
	p[((offset-256)<<2) + 2] = cb;

	p[((offset+256)<<2)    ] = cr;
	p[((offset+256)<<2) + 1] = cg;
	p[((offset+256)<<2) + 2] = cb;

	FXtime ++;

}

// ---------------------------------------------------------------------------
// 
// Freedir Tunnel, Sphere, Plans Stuff !!!!
//
// ---------------------------------------------------------------------------

// Structures
typedef struct Vector
{
	float x,y;
}Vector;

typedef struct Point
{
	unsigned short x,y;
	UCHAR coul[4]; 
}Point;

typedef struct Vector3D
{
	float x,y,z;
}Vector3D;

typedef struct Vector2D
{
	int x,y;
}Vector2D;

typedef struct Point3D         
{           
	float x,y,z;
}Point3D;

typedef float Matrix [3][3];

// Globals 
unsigned short px = 0,py = 0;
unsigned short opx = 0,opy = 0;
unsigned short vx,vy;
unsigned short t_max = 255;
unsigned short t_min = -255;
unsigned short s;

unsigned long pos;	
Point interpol[257][257];
Vector3D d;              
Vector3D camera = { 0, 0, 0 };
Matrix cam_matrix = { { 0, 0, 0 }, { 0, 0, 0} };

void NormalizeVector( Vector3D *v )
{
	float l;

	l = (float)sqrt( (v->x*v->x) + (v->y*v->y) + (v->z*v->z) );

	v->x /= ( l ? l : 1 );
	v->y /= ( l ? l : 1 );   
	v->z /= ( l ? l : 1 );
}
      
void RotateMatrix( Matrix m, float rx, float ry, float rz)
{
  float cosx, sinx;
  float cosy, siny;  
  float cosz, sinz;

  cosx = (float) cos(rx);
  sinx = (float) sin(rx);
  cosy = (float) cos(ry);
  siny = (float) sin(ry);
  cosz = (float) cos(rz);       
  sinz = (float) sin(rz);
        
  m[0][0] = cosy * cosz;
  m[0][1] = cosy * sinz;
  m[0][2] = -siny;

  m[1][0] = sinx * siny * cosz - cosx * sinz;
  m[1][1] = sinx * siny * sinz + cosx * cosz;
  m[1][2] = sinx * cosy;
       
  m[2][0] = cosx * siny * cosz + sinx * sinz;
  m[2][1] = cosx * siny * sinz - sinx * cosz;
  m[2][2] = cosx * cosy;

}
        
void VectorMulMatrix( Vector3D *v, Vector3D *v1, Matrix m1)
{
   int i;
   float temp[3] = { 0, 0, 0 };

   for( i=0; i<3; i++ )      
   {           
     temp[i] += v1->x * m1[i][0];
     temp[i] += v1->y * m1[i][1];
     temp[i] += v1->z * m1[i][2];
   }

   v->x = temp[0];
   v->y = temp[1];      
   v->z = temp[2];
}

void DoInterpolation()
{
	int _x, _y;             
    int x, y;               
    unsigned long  v;       
    unsigned long _v;       
    unsigned long dvdx;     
    unsigned long dvdy;     
    unsigned long dvdxdy;   
    unsigned long v1, v2, v3, v4;   

	for(_y=0;_y<Y2D;_y+=8)      
	{
		for(_x=0;_x<X2D;_x+=8)    
		{
			v1 = interpol[_x][_y].x<<8;     
            v2 = interpol[_x+8][_y].x<<8;   
            v3 = interpol[_x][_y+8].x<<8;   
            v4 = interpol[_x+8][_y+8].x<<8; 

            _v = v = v1;                 

            dvdx   = (-(long)v1+v2)>>3;        
			dvdy   = (-(long)v1+v3)>>3;        
			dvdxdy = (v1-v2-v3+v4)>>6;   

			for(y=_y;y<_y+8;y++) 
			{       
				for(x=_x;x<_x+8;x++) 
				{     
					v += dvdx;               
					interpol[x][y].x =(USHORT) (v>>8);
				}

			_v += dvdy;                
			v = _v;                    
			dvdx += dvdxdy;            
			}
		}
	}
		
	for(_y=0;_y<Y2D;_y+=8)      
	{
		for(_x=0;_x<X2D;_x+=8)    
		{
			v1 = interpol[_x][_y].y<<8;     
			v2 = interpol[_x+8][_y].y<<8;   
			v3 = interpol[_x][_y+8].y<<8;   
			v4 = interpol[_x+8][_y+8].y<<8; 

			_v = v = v1;                 

			dvdx   = (-(long)v1+v2)>>3;        
			dvdy   = (-(long)v1+v3)>>3;        
			dvdxdy = (v1-v2-v3+v4)>>6;   

			for(y=_y;y<_y+8;y++) 
			{       
				for(x=_x;x<_x+8;x++) 
				{     
					v += dvdx;               
					interpol[x][y].y = (USHORT) (v>>8); 
				}
				_v   += dvdy;              
				v     = _v;                
				dvdx += dvdxdy;            
			}
		}
	}
}

void FreeDirectionalTunnel()
{           
    Vector3D inter;
    int x, y;       
    float a, b, c;           
    float delta;         
    float t, t1, t2;   
    static int rayon = 128;
   
    c = (camera.x * camera.x) + (camera.y * camera.y) - (rayon * rayon);

    for( y=0; y<Y2D+1; y+=8 )
	{
      for( x=0; x<X2D+1; x+=8 )
      {
		  d.x = ((float)(x-(X2D>>1)))/120;
          d.y = ((float)(y-(Y2D>>1)))/120;
          d.z = 1;      
         
		  VectorMulMatrix( &d, &d, cam_matrix );

          NormalizeVector( &d );
    
          a = ( d.x * d.x ) + ( d.y * d.y );
          b = 2 * ( camera.x * d.x + camera.y * d.y);
       
          delta = ( b * b ) - 4 * a * c;
                 
	     if(delta>0) 
		 {
			 delta = (float)sqrt( delta );
       
             t1 = ( -b - delta ) / ( 2 * a );
        
			 if( t1 > 0 ) 
				 t = t1;
         
			 else
			 {
				 t2 = ( -b + delta ) / ( 2 * a );
         
		         t = t2;
			 }

			 inter.x = camera.x + t * d.x;
             inter.y = camera.y + t * d.y;
             inter.z = camera.z + t * d.z;

			 interpol[x][y].x = (unsigned short) (fabs( (256 * atan2(inter.y, inter.x) ) / PI) );
			 interpol[x][y].y = (unsigned short)  fabs( inter.z );
		 }
		 
		 else
		 {
			 interpol[x][y].x = 0;
             interpol[x][y].y = 0;
		 }
		 
	  }
	}
}

void Effect2D::writeRayPrim(  )
{
			camera.z += 15;
			camera.x = (float)(40*cos(alpha/2) + 30*sin(alpha/3));
			camera.y = (float)(50*sin(alpha/4) + 20*cos(alpha));

			// camera.z = 0;
			// camera.x = 0;
			// camera.y = 0;
			
			alpha = (float)time/50;
        
//			if ( LOWORD(pos) >= 6 && LOWORD(pos) < 8 )
			{
				cam_rotx += 0.013f;            
				cam_roty += 0.009f;               
				cam_rotz += 0.004f;        
			}       
          
			RotateMatrix( cam_matrix, cam_rotx, cam_roty, cam_rotz);
			FreeDirectionalTunnel();
			DoInterpolation();   
			DrawFDObject( 2 );
}

void Effect2D::DrawFDObject( long textu )
{
	int x,y;
	ULONG *d_dat;
	ULONG *m_dat;

	d_dat = (ULONG *)videoBuffer;
	m_dat = (ULONG *)maps[textu].data;

	for ( y=0; y<Y2D; y++ )
	{                      
		for ( x=0; x<X2D; x++ )
		{
			d_dat[x+y*X2D]  = (m_dat[(interpol[x][y].y * 256 + interpol[x][y].x)&0xffff]);
/*
			// deep fog
			ULONG deep = (interpol[x][y].y  - (ULONG)camera.z) >> 3;
			if( deep > 255) deep = 255;
			d_dat[x+y*X2D] |= (~deep)<<24;
*/
		}
	}
}

void Effect2D::CalculateFlower()
{
	int x,y;
	int cx, cy;

	Flower = new int[X2D*Y2D*4];

	cx = X2D;
	cy = Y2D;

	for ( y=0; y<Y2D*2; y++)
	{
		for ( x=0; x<X2D*2; x++ )
		{
			Flower[ x + y * (X2D*2) ] = (int)( ( 1.0 * cos( 18 * atan2( (y-cy), (x-cx) )) * 255 / (PI*2) +
						           0.3 * sin( 15  * atan2( (y-cy), (x-cx) )) * 255 / (PI*2) +
				                          sqrt( (cy-y) * (cy-y) + (cx-x) * (cx-x)  ) ));
		}
	}          
}

void Effect2D::writeFlower( long text )
{
	int x,y;
	int center_x = maps[text].sizex >> 1; 
	int center_y = maps[text].sizey >> 1;
	int flower_w = maps[text].sizex * 2 ;

	ULONG *dst;
	ULONG *m;
	ULONG f_time = time>>3;

	dst = (ULONG *)videoBuffer;
	m   = (ULONG *)maps[text].data;

	int x1 = (int)(center_x +  120 * sin( f_time / 110.8f ));
	int y1 = (int)(center_y +    90 * cos( f_time / 120.3f ));
	int offset1 =  x1 + y1 * flower_w;

	int x2 = (int)(center_x +  120 * sin( f_time / 200.8f ));
	int y2 = (int)(center_y +   90 * cos( f_time / 50.34f ));
	int offset2 =   x2 + y2 * flower_w;

	int x3 = (int)(center_x +  120 * sin( f_time / 180.91f ));
	int y3 = (int)(center_y +    90 * cos( f_time / 150.12f ));
	int offset3 =   x3 + y3 * flower_w;

	for ( y=0; y<Y2D; y++ )
	{
		for ( x=0; x<X2D; x++ )
		{
			dst[ x+ y * X2D] = m[ x +  (( (Flower[ x + (y&255) * flower_w + offset1 ]&255) + 
                                                         (Flower[ x + (y&255) * flower_w + offset2 ]&255) +
							 (Flower[ x + (y&255) * flower_w + offset3 ]&255) )&255) * 255  ];

		}
	}
}


void Effect2D::RadialBlurFX(  int dx, int dy, float intensity )
{
	int x, y;
	int l = 0;
	int c2 = (int)intensity;
	unsigned char cr, cb, cg, pr, pg, pb, fr, fg, fb;
	UCHAR *p = (uchar*)videoBuffer;

	for ( y=dy; y>=0; y-- )
	{
		int k1 = y - ( l / (256*256));
		k1 *= 256;
		int i2 = 0;
		int k2 = y * 256;      

		for ( x=dx; x>=0; x-- )
		{	
			cr =  p[((k2 + x)<<2)];       
			cg = p[((k2 + x)<<2)+1];
			cb = p[((k2 + x)<<2)+2];

			pr =  p[(((x -   (i2 / (256*256) )) + k1)<<2)];
			pg = p[(((x -   (i2 / (256*256) )) + k1)<<2)+1];
			pb = p[(((x -   (i2 / (256*256) )) + k1)<<2)+2];

			fr =  ( cr + pr ) >> 1; 
			fg = ( cg + pg ) >> 1; 
			fb = ( cb + pb ) >> 1; 

			p[((k2 + x)<<2)       ] = fr;
			p[((k2 + x)<<2) + 1] = fg;
			p[((k2 + x)<<2) + 2] = fb;     

			i2 -= c2;
		}             
		i2 = c2;

		for ( x = dx+1; x<256; x++)
		{
			cr =  p[((k2 + x)<<2)];       
			cg = p[((k2 + x)<<2)+1];
			cb = p[((k2 + x)<<2)+2];

			pr =  p[(((x -   (i2 / (256*256) )) + k1)<<2)];
			pg = p[(((x -   (i2 / (256*256) )) + k1)<<2)+1];
			pb = p[(((x -   (i2 / (256*256) )) + k1)<<2)+2];

			fr =  ( cr + pr ) >> 1; 
			fg = ( cg + pg ) >> 1; 
			fb = ( cb + pb ) >> 1; 

			p[((k2 + x)<<2)       ] = fr;
			p[((k2 + x)<<2) + 1] = fg;
			p[((k2 + x)<<2) + 2] = fb;     

			i2 += c2;
		}      
		l -= c2;
	}
	l = c2;
            
	for ( y=dy+1; y<256; y++ )
	{
		int l1 = y - 1 -( l / (256*256));
		l1 *= 256;
		int j2 = 0;
		int l2 = y * 256;          

		for ( x=dx; x>=0; x-- )
		{
			cr =  p[((l2 + x)<<2)];       
			cg = p[((l2 + x)<<2)+1];
			cb = p[((l2 + x)<<2)+2];

			pr =  p[(((x -   (j2 / (256*256) )) + l1)<<2)];
			pg = p[(((x -   (j2 / (256*256) )) + l1)<<2)+1];
			pb = p[(((x -   (j2 / (256*256) )) + l1)<<2)+2];

			fr =  ( cr + pr ) >> 1; 
			fg = ( cg + pg ) >> 1; 
			fb = ( cb + pb ) >> 1; 

			p[((l2 + x)<<2)       ] = fr;
			p[((l2 + x)<<2) + 1] = fg;
			p[((l2 + x)<<2) + 2] = fb; 

			j2 -= c2;
		}
		j2 = c2;     

		for ( x=dx+1; x<256; x++ )
		{        
			cr =  p[((l2 + x)<<2)];       
			cg = p[((l2 + x)<<2)+1];
			cb = p[((l2 + x)<<2)+2];

			pr =  p[(((x -   (j2 / (256*256) )) + l1)<<2)];
			pg = p[(((x -   (j2 / (256*256) )) + l1)<<2)+1];
			pb = p[(((x -   (j2 / (256*256) )) + l1)<<2)+2];

			fr =  ( cr + pr ) >> 1; 
			fg = ( cg + pg ) >> 1; 
			fb = ( cb + pb ) >> 1; 

			p[((l2 + x)<<2)       ] = fr;
			p[((l2 + x)<<2) + 1] = fg;
			p[((l2 + x)<<2) + 2] = fb; 

			j2 += c2;
		}
		l += c2;     
	}
}

void glFace(float dx1, float dx2, float dy1, float dy2)
{
	glTexCoord2f( 0, 1);
	glVertex3f(-1-dx1, 1+dy2,-1);
	glTexCoord2f( 1, 1);
	glVertex3f( 1+dx2, 1+dy2,-1);
	glTexCoord2f( 1, 0);
	glVertex3f( 1+dx2,-1-dy1,-1);
	glTexCoord2f( 0, 0);
	glVertex3f(-1-dx1,-1-dy1,-1);
}

void Effect2D::RadialBlurHard(float x1, float x2, float y1, float y2, float alpha)
{
	float dx1=0, dy1=0,dx2=0,dy2=0;
	initProjFor2D();

	glBindTexture(GL_TEXTURE_2D, GLtexture[videoGLnum]);
	glTexSubImage2D (GL_TEXTURE_2D, 0, 0,0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, videoBuffer);
	glLoadIdentity();		// current matrix is MODEL_MATRIX
	glColor4f( 1, 1, 1, alpha);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glDepthMask( false );
	glBegin(GL_QUADS);

	glTexCoord2f( 0, 1);
	glVertex3f(-1, 1,-1);
	glTexCoord2f( 1, 1);
	glVertex3f( 1, 1,-1);
	glTexCoord2f( 1, 0);
	glVertex3f( 1,-1,-1);
	glTexCoord2f( 0, 0);
	glVertex3f(-1,-1,-1);

	long nFaceGL = 5;
	float alphaP = alpha / nFaceGL;
	for( long jop = 0;jop<nFaceGL;jop++)
	{
		alpha -= alphaP;
		dx1 += x1;
		dx2 += x2;
		dy1 += y1;
		dy2 += y2;
		glColor4f( 1, 1, 1, alpha);
		glFace( dx1, dx2, dy1, dy2);
	}
	glEnd();
	glDepthMask( true );
	glDisable(GL_TEXTURE_2D);

	initProjFor3D();
}

#endif