/**
 * File:	locGL.cc
 * Author:	Markus Trenkwalder & Henning Scharsach
 * Created:	2003.12.03
 * Description:
 *
 *
 * Changes:
 * Date		 Who		What
 * --------------------------------------------------------------------------
 * 05.1.2004 HS			Maussteuerung
 * 11.1.2004 HS			Vertex & Pixel-Shader Setup
 * 14.1.2004 HS			WaveMap
 * 16.1.2004 HS			Vertex Shader Waves
 * 18.1.2004 HS			Texture Loader
 * 22.1.2004 HS			Normals, Rain, Drawing
 * 24.1.2004 MT			Code cleanup and inclusion of MAxxis code
 **/

#include "stdafx.h"
#include "locGL.h"
#include "math.h"
#include "engine.h"
#include "gl_ext.h"
//#include <conio.h>

#include <cg/cg.h>
#include <cg/cgGL.h>
#include <GL/glext.h>

GLfloat angle = 0.0;
GLfloat yaw = 0.0;
float time, time2 = 0.0;
float refresh = 0.0;
GLfloat distance = 0.0;
bool simplefragment = true;

HDC hDC = 0;
GLfloat value = 0.0;

float WaveMap[2][100][100];
float NormalMap[100][100][3];
int oldmap = 0;
int newmap = 1;
int temp;
int errnum = 0;
bool raindraw = FALSE;
GLuint seaTexture;

Texture* water[32];
Texture* rasen;

int wateranim = 0;

int ANZRAIN = 10;

struct rainstruct{
	float x,y,z;
	float speed;
} rain[2000];

CGcontext context;
CGprogram vertexProgram, fragmentProgram, fragmentProgram2;
CGprofile vertexProfile, fragmentProfile;
CGparameter position, color, texCoord, baseTexture, someColor,modelViewProj, modelView, modelViewIT, constants;

float ambient[] = { 1.0, 1.0, 1.0, 1.0 };
float light1_col[] = { 1.0, 1.0, 1.0, 1.0 };
float light1_pos[] = { 0.0, 10.0, 10.0, 0.0 };

World* theWorld;
Timer* theTimer;

int pbufferid;
bool renderToPBuffer = false;


// ----------------------------------------------------------------------------
//
GLuint LoadTexture( const char * filename )
{
    GLuint texture;
    BYTE * data;
    FILE * file;
	file = fopen( filename, "rb" );
    if ( file == NULL ) return 0;
    data = new BYTE [ 256 * 256 * 3 ];
    fread( data, 256 * 256 * 3, 1, file );
    fclose( file );
    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, 256, 256, GL_RGB, GL_UNSIGNED_BYTE, data );
    free( data );
    return texture;
}


// ----------------------------------------------------------------------------
//
void cgErrorCallback(char* s) {
	errnum++;

	CGerror lastError = cgGetError();
	if(lastError) {
		glClearColor(1.0f, 0.0f, 0.0f, 0.5f);
		fprintf(stderr, "%d: %s: %s %s\n", errnum, s, cgGetErrorString(lastError), cgGetLastListing(context) );
	} else {
		fprintf(stderr, "%d: %s: OK!\n", errnum, s);
	}
}

// ----------------------------------------------------------------------------
//
void InitShaders()
{
	// Create context
	context = cgCreateContext();
	
	cgErrorCallback("create context");

	// Initialize profiles and compiler options
	vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	//vertexProfile = CG_PROFILE_VS_1_1;
	//cgGLEnableProfile(vertexProfile);
	//cgGLSetOptimalOptions(vertexProfile);
	fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	//fragmentProfile = CG_PROFILE_ARBFP1;
	cgGLSetOptimalOptions(fragmentProfile);
	cgErrorCallback("get profiles");

	// Create the vertex program
	vertexProgram = cgCreateProgramFromFile( context, CG_OBJECT, "vs.asm", vertexProfile, "vs", NULL );
	
	cgErrorCallback("create vertex program");

	// Load the program
	cgGLLoadProgram(vertexProgram);
	cgErrorCallback("load vertex program");

	// Create the fragment program
	fragmentProgram = cgCreateProgramFromFile( context, CG_OBJECT, "ps.asm", fragmentProfile, "ps", NULL );
	cgErrorCallback("create fragment program");

	// Load the program
	cgGLLoadProgram(fragmentProgram);
	cgErrorCallback("load fragment program");

	fragmentProgram2 = cgCreateProgramFromFile( context, CG_OBJECT, "ps2.asm", fragmentProfile, "ps2", NULL );
	cgErrorCallback("create fragment2 program");

	cgGLLoadProgram(fragmentProgram2);
	cgErrorCallback("load fragment program 2");

	//enable profile
	cgGLEnableProfile(vertexProfile);
	
	//bind the program
	cgGLBindProgram(vertexProgram);

	//enable profile
	cgGLEnableProfile(fragmentProfile);

	//bind the program
	cgGLBindProgram(fragmentProgram2);

	modelViewProj = cgGetNamedParameter(vertexProgram,"ModelViewProj");
	modelView = cgGetNamedParameter(vertexProgram,"ModelView");
	modelViewIT = cgGetNamedParameter(vertexProgram,"ModelViewIT");
}


// ----------------------------------------------------------------------------
//
void InitPBuffer()
{
	GL_initPBuffer();	// This binds the extention functions.
	GL_InitPbufferHandling(0);

	pbuffer_create_info_s createinfo;

	createinfo.flags        = PBCREATE_TEXBIND;
	createinfo.width        = 512;    // Breite des Buffers
	createinfo.height       = 512;   // Höhe des Buffers
	createinfo.bits_red     = 8;     // Bits pro Pixel
	createinfo.bits_green   = 8;
	createinfo.bits_blue    = 8;
	createinfo.bits_alpha   = 8;
	createinfo.bits_depth   = 24;
	createinfo.bits_stencil = 8;
	createinfo.tex_format   = PBFORMAT_RGBA;
	createinfo.tex_target   = PBTARGET_TEX_2D;

	pbufferid = GL_CreatePbuffer( &createinfo );
}

// ----------------------------------------------------------------------------
//
void locInit(HDC hdc)
{
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	//glCullFace(GL_BACK);

	water[0]  = new Texture("water00.bmp");
	water[1]  = new Texture("water01.bmp");
	water[2]  = new Texture("water02.bmp");
	water[3]  = new Texture("water03.bmp");
	water[4]  = new Texture("water04.bmp");
	water[5]  = new Texture("water05.bmp");
	water[6]  = new Texture("water06.bmp");
	water[7]  = new Texture("water07.bmp");
	water[8]  = new Texture("water08.bmp");
	water[9]  = new Texture("water09.bmp");
	water[10] = new Texture("water10.bmp");
	water[11] = new Texture("water11.bmp");
	water[12] = new Texture("water12.bmp");
	water[13] = new Texture("water13.bmp");
	water[14] = new Texture("water14.bmp");
	water[15] = new Texture("water15.bmp");
	water[16] = new Texture("water16.bmp");
	water[17] = new Texture("water17.bmp");
	water[18] = new Texture("water18.bmp");
	water[19] = new Texture("water19.bmp");
	water[20] = new Texture("water20.bmp");
	water[21] = new Texture("water21.bmp");
	water[22] = new Texture("water22.bmp");
	water[23] = new Texture("water23.bmp");
	water[24] = new Texture("water24.bmp");
	water[25] = new Texture("water25.bmp");
	water[26] = new Texture("water26.bmp");
	water[27] = new Texture("water27.bmp");
	water[28] = new Texture("water28.bmp");
	water[29] = new Texture("water29.bmp");
	water[30] = new Texture("water30.bmp");
	water[31] = new Texture("water31.bmp");

	if (!theWorld) {
		Texture* tex = new Texture("panorama1.bmp");
		theTimer = new Timer(10);
		theWorld = new World(hdc);
		Horizont *h = new Horizont(tex, 90.0, 18);
		h->setPosition(0.0, 1.5, 0.0);
		theWorld->addObject(h);
		tex = new Texture("rasen.bmp");
		rasen = new Texture("rasen.bmp");
		Terrain *t = new Terrain("HeightMap.bmp", 7.0, tex);
		t->setPosition(-64.0, -1.20, -64.0);
		theWorld->addObject(t);
	}

	//glEnable(GL_CULL_FACE);
	for (int i=0; i<2000; i++) {
		rain[i].x = ((float)rand()/(float)RAND_MAX)*30 - 15.0f;
		rain[i].z = ((float)rand()/(float)RAND_MAX)*30 - 15.0f;
		rain[i].y = (float)(rand() % 100)/10;
		rain[i].speed = (float)(rand() % 100)/(float)500 + 0.4f;
	}

	InitShaders();
	InitPBuffer();
	
	glEnable( GL_TEXTURE_2D );
	seaTexture = LoadTexture("panorama.raw");

	// setup OpenGL extensions:

	/*
	char** name = gl_ext_function_names;
	std::string exts;
	while (*name) {
		if (wglGetProcAddress(*name)) {
			exts += std::string(*name) + "() ";
		}
		++name;
	}
	MessageBox(NULL, exts.c_str(), "Info", MB_OK);

	wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)wglGetProcAddress("wglGetPbufferDCARB");
	wglGetPbufferDCARB( (HPBUFFERARB)0 );
	*/
}

// ----------------------------------------------------------------------------
//
float func(float x, float y)
{
	float z = sqrt(x*x + y*y);
	float z2 = sqrt((x-2)*(x-2) + (y+2)*(y+2));
	z2 = sqrt(x*x + y*y);
//	return (sin(5*z-time/5)/6+cos(6*z2-time/2)/15)/(time/40 + 1);
	return (sin(5*z-time/5)/6+cos(6*z2-time/2)/15)/2;
}


// ----------------------------------------------------------------------------
//
void UpdateWaveMap()
{
	int x,y;
	float t, dx, dy, dz, l;
	int rx,ry;
	wateranim++;

	if (wateranim>31) {wateranim = 0;}

	if (raindraw) {
		for (int i=0; i<ANZRAIN; i++) {
			rain[i].y -= rain[i].speed;
			if (rain[i].y<0) {
				rain[i].y = 10.0f;
				rain[i].x = ((float)rand()/(float)RAND_MAX)*30 - 15.0f;
				rain[i].z = ((float)rand()/(float)RAND_MAX)*30 - 15.0f;
			}
			if (rain[i].y<0.8) {
				rx = (rain[i].x+5)*10;
				ry = (rain[i].z+5)*10;
				if (rx>1 && rx<98 && ry>1 && ry<98)
				{ WaveMap[oldmap][(int)((rain[i].x+5)*10)][(int)((rain[i].z+5)*10)] = -0.05f; }
			}
		}
	}

	for (y=1;y<99;y++) {
		for (x=1;x<99;x++) {
			t = ((WaveMap[oldmap][x-1][y] + WaveMap[oldmap][x+1][y] + WaveMap[oldmap][x][y-1] + 
				WaveMap[oldmap][x][y+1] ) / 2) - WaveMap[newmap][x][y];
			t = t *0.93f;
			WaveMap[newmap][x][y] = t;
		}
	}

	for (y=1;y<99;y++) {
		for (x=1;x<99;x++) {
			dx = WaveMap[oldmap][x+1][y]-WaveMap[oldmap][x-1][y];
			dy = -1;
			dz = WaveMap[oldmap][x][y+1]-WaveMap[oldmap][x][y-1];
			l = sqrt(dx*dx + dy*dy + dz*dz);
			
			NormalMap[x][y][0] = dx/l;
			NormalMap[x][y][1] = dy/l;
			NormalMap[x][y][2] = 1/l;
		}
	}
}

// ----------------------------------------------------------------------------
//
void locRender()
{
	int a,i;
	float x,y,z;
	//float dx1, dy1, dz1, dx2, dy2, dz2;
	y = 1.0;

	/*if (renderToPBuffer) {
		GL_BindPbuffer(pbufferid);
	} else {
		GL_UnbindPbuffer();
	}*/

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	
	angle = (float) dx / 4;
	angle = (angle>360.0f) ? angle-360.0 : angle;
	angle = (angle<0.0f) ? angle+360.0 : angle;
	yaw = (float) dy / 3.0f;
	distance = (GLfloat) dz / 50.0f;
	
	if (time>6.283184f) {
		time -= 6.283184f;
	}

	time += 0.1f;

	//glTranslatef(0.0f,-5.0f,-0.0f);
	glRotatef(yaw, 1.0f, 0.0f, 0.0f);
	glRotatef(angle, 0.0f, 1.0f, 0.0f);

	glPolygonMode(GL_FRONT, GL_FILL);
	glPolygonMode(GL_BACK, GL_LINE);

	//if (renderToPBuffer) 
	{
		GL_BindPbuffer(pbufferid);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(30.0f,(GLfloat)512/(GLfloat)512,1.0f,200.0f);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		gluLookAt(0, 0, 10, 
		  0, 0, 0, 
		  0, 1, 0);
	    glTranslatef(0.0f,-5.0f,-0.0f);
		glRotatef(yaw, 1.0f, 0.0f, 0.0f);
		glRotatef(angle, 0.0f, 1.0f, 0.0f);

		theWorld->draw(false);
		GL_UnbindPbuffer();

		GL_BindPbufferTexture( 0, pbufferid, 0, false);
		glEnable(GL_TEXTURE_2D);
		glBegin(GL_POLYGON);
			glTexCoord2f(0.0f,  0.0f);
			glVertex3f( -5.0f,  2.0f, -10.0f);
			glTexCoord2f(1.0f,  0.0f);
			glVertex3f(  5.0f,  2.0f, -10.0f);
			glTexCoord2f(1.0f,  1.0f);
			glVertex3f(  5.0f, 12.0f, -10.0f);
			glTexCoord2f(0.0f,  1.0f);
			glVertex3f( -5.0f, 12.0f, -10.0f);
		glEnd();
		//GL_UnbindPbufferTexture(pbufferid);
		//glDisable(GL_TEXTURE_2D);

	}

    cgGLEnableProfile(vertexProfile);
	cgGLEnableProfile(fragmentProfile);
	

	refresh = theTimer->getMS();

	if (refresh>0.0001) 
	{
		theTimer->update();
		UpdateWaveMap(); 
		temp = oldmap;
		oldmap = newmap;
		newmap = temp;
	}

	cgGLSetStateMatrixParameter(modelViewProj,CG_GL_MODELVIEW_PROJECTION_MATRIX,CG_GL_MATRIX_IDENTITY);
	cgGLSetStateMatrixParameter(modelView, CG_GL_MODELVIEW_MATRIX,CG_GL_MATRIX_IDENTITY);
	cgGLSetStateMatrixParameter(modelViewIT, CG_GL_MODELVIEW_MATRIX,CG_GL_MATRIX_INVERSE_TRANSPOSE);

	constants = cgGetNamedParameter(vertexProgram, "Constants");
	cgGLSetParameter3f(constants, time, 0.4-yaw/100, 0);

	glColor3f(1.0, 1.0, 1.0);
	for (a=0; a<99; a++)
	{
		glActiveTextureARB(2);
		glBindTexture( GL_TEXTURE_2D, rasen->getTexture());
		glActiveTextureARB(1);
		glBindTexture( GL_TEXTURE_2D, water[wateranim]->getTexture());
		glActiveTextureARB(0);
		glBindTexture( GL_TEXTURE_2D, seaTexture);
		glBegin(GL_TRIANGLE_STRIP);
		for (i=0; i<100; i++) {
			y = -5.0f + a * 0.1f;
			x = -5.0f + i * 0.1f;
			z = WaveMap[oldmap][i][a];

			//glColor3f(z*2, z*2, 0.3);
			glTexCoord2f((float)a/(float)50, (float)i/(float)50);
			glNormal3f(NormalMap[i][a][0], NormalMap[i][a][1], NormalMap[i][a][2]);
			glVertex3f(x, z, y);
			z = WaveMap[oldmap][i][a+1];
			//glColor3f(z*2, z*2, 0.3);
			glTexCoord2f((float)a/(float)50+0.02f, (float)i/(float)50);
			glNormal3f(NormalMap[i][a+1][0], NormalMap[i][a+1][1], NormalMap[i][a+1][2]);
			glVertex3f(x, z, y+0.1f);
		}
		glEnd();
	}

	cgGLDisableProfile(vertexProfile);
	cgGLDisableProfile(fragmentProfile);

	if (raindraw) {
		glPolygonMode(GL_FRONT, GL_LINE);
		glColor3f(0.7f, 0.7f, 0.8f);
		glBegin(GL_LINES);
		for (int i=0; i<ANZRAIN; i++) {
			glVertex3f(rain[i].x,rain[i].y, rain[i].z);
			glVertex3f(rain[i].x,rain[i].y+0.5f, rain[i].z);
		}
		glEnd();
	}
	theWorld->draw(true);
	glColor3f(1.0f, 1.0f, 1.0f);

}

// ----------------------------------------------------------------------------
//
void locResize(int w, int h)
{
	if (h==0) {	h=1; }

	glViewport(0,0,w,h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	gluPerspective(65.0f,(GLfloat)w/(GLfloat)h,1.0f,200.0f);
	//glFrustum(-1.0*w/h, 1.0*w/h, -1.0, 1.0, 1.0, 20.0);
	glTranslatef(0.0f, -0.25f, distance);
	glRotatef(15.0, 1.0, 0.0, 0.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}