#define NO_SDL_GLEXT

#include <GL/glew.h>
#include <SDL/SDL_OpenGL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <math.h>

#include "demo_script.h"
#include "demo_textures.h"
#include "demo_meshes.h"
#include "demo_shaders.h"
#include "demo_screen.h"
#include "demo_timer.h"

#include "math_vector3D.h"

#include "part_water.h"

GLuint bufferTextureWater;
GLuint imageTextureWater;

GLint wtr_buffer_disp;
GLint img_buffer;

GLint lpx, lpy;
GLint water_bump;

int buffer_width;
int buffer_height;
int buffer_size;

int *wbuffer1;
int *wbuffer2;

bool water_init = false;

int programm_water_disp = PR_WATER_DISPLACEMENT;

unsigned int frame_select = 0;
const int num_drops = 16384;
int water_drops_show = 256;

float div1[num_drops];
float div2[num_drops];
int mul1[num_drops];
int mul2[num_drops];

bool water_bump_state = true;

void water_precalcs()
{
	buffer_width = ScreenWidth / 2;
	buffer_height = ScreenHeight / 2;
	buffer_size = buffer_width * buffer_height;

	wbuffer1 = new int[buffer_size];
	wbuffer2 = new int[buffer_size];

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glShadeModel(GL_SMOOTH);

	glActiveTextureARB(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	bufferTextureWater = GenTexture(ScreenWidth, ScreenHeight, GEN_EMPTY, 2);
	glBindTexture(GL_TEXTURE_2D, bufferTextureWater);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

	glActiveTextureARB(GL_TEXTURE1);
	glEnable(GL_TEXTURE_2D);
	imageTextureWater = GenTexture(ScreenWidth, ScreenHeight, GEN_VORONOI, 2);
	glBindTexture(GL_TEXTURE_2D, imageTextureWater);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

	wtr_buffer_disp = MyGetUniformLocation(programm_water_disp, "wbuffer_disp");
	img_buffer = MyGetUniformLocation(programm_water_disp, "ibuffer");

	water_bump = MyGetUniformLocation(programm_water_disp, "water_bump");
	lpx = MyGetUniformLocation(programm_water_disp, "lpx");
	lpy = MyGetUniformLocation(programm_water_disp, "lpy");

	float slowness = 2.0;
	for (int i=0; i<num_drops; i++)
	{
		div1[i] = (float)(512.0 + rand() % 1024) * slowness;
		div2[i] = (float)(512.0 + rand() % 1024) * slowness;

		mul1[i] = buffer_width / 4 + rand() % (int)(buffer_width/4);
		mul2[i] = buffer_height / 4 + rand() % (int)(buffer_height/4);
	}

    water_init = true;
}

void DrawTestWater(int px, int py)
{
	int size = 1;
	int loop = (size + 1) / 2;
	for (int y=-loop; y<loop; y++)
	{
		for (int x=-loop; x<loop; x++)
		{
			int xpp = x + px;
			int ypp = y + py;

			if (xpp < 0) xpp = 0;
			if (ypp < 0) ypp = 0;
			if (xpp > buffer_width - 1) xpp = buffer_width - 1;
			if (ypp > buffer_height - 1) ypp = buffer_height - 1;

			int i = xpp + ypp * buffer_width;

			wbuffer1[i] = 255;
		}
	}
}

void DrawQuadWater(float l)
{
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f); glVertex2f(-l, -l);
		glTexCoord2f(1.0f, 0.0f); glVertex2f( l, -l);
		glTexCoord2f(1.0f, 1.0f); glVertex2f( l, l);
		glTexCoord2f(0.0f, 1.0f); glVertex2f(-l, l);
	glEnd();
}

#define shite -2.41425f

void MakeDrops(int ptime)
{
	for (int i=0; i<water_drops_show; i++)
	{
		int px = buffer_width/2 + (int)(sin((float)ptime / div1[i]) * mul1[i]);
		int py = buffer_height/2 + (int)(sin((float)ptime / div2[i]) * mul2[i]);

		DrawTestWater(px, py);
	}

	if (IsIterReady(1))
	{
		glLoadIdentity();
		glTranslatef((float)(rand() % 32) - 16.0f, (float)(rand() % 32) - 16.0f, shite * 16.0f);

		int px = buffer_width/16 + rand() % (int)(buffer_width/1.125);
		int py = buffer_height/16 + rand() % (int)(buffer_height/1.125);

		DrawTestWater(px, py);
	}
}

void SwapBuffers()
{
	int *temp = wbuffer2;
	wbuffer2 = wbuffer1;
	wbuffer1 = temp;
}

void MakeWater(int ptime)
{
	for (int i = 1 + buffer_width; i < buffer_size - buffer_width - 1; i++)
	{
		wbuffer2[i] = ((wbuffer1[i-1] + wbuffer1[i+1] + wbuffer1[i-buffer_width] + wbuffer1[i+buffer_width]) >> 1) - wbuffer2[i];
		wbuffer2[i] -= (wbuffer2[i] >> 5);
		if (wbuffer2[i] < 0 ) wbuffer2[i] = 0;
		if (wbuffer2[i] > 255) wbuffer2[i] = 255;
	}

	glActiveTextureARB(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, bufferTextureWater);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, buffer_width, buffer_height, 0,	GL_RGBA, GL_UNSIGNED_BYTE, wbuffer1);

	SwapBuffers();
}

void DrawWater(int ptime, int bufnum)
{
	// make drops on buffers
	MakeDrops(ptime);

	MakeWater(ptime);

	MyUseProgram(programm_water_disp);
	MyUniform1i(wtr_buffer_disp, 0);
	MyUniform1i(img_buffer, 1);

	MyUniform1i(water_bump, water_bump_state);

	float light_px = (float)(sin(ptime / 1048.0) * 0.45);
	float light_py = (float)(sin(ptime / 833.0) * 0.35);
	MyUniform1f(lpx, light_px);
	MyUniform1f(lpy, light_py);

	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, shite * 16.0f);
	DrawQuadWater(16.0f);
}

void water_run(int ptime)
{
	if (!water_init) water_precalcs();

	DrawWater(ptime, frame_select);

	frame_select = (frame_select + 1) & 1;
}
