#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 "project_textures.h"
#include "project_timer.h"
#include "math_vector3D.h"
#include "math_defs.h"

#include "fx_colors.h"


void GenTextureBall(int width, int height, unsigned int* data)
{
	float r = width / 2.75f;
	int i = 0;
	for (float y=-height/2.0f; y<height/2.0f; y++)
	{
		for (float x=-width/2.0f; x<width/2.0f; x++)
		{
			float z = 255;
			float zz = r*r - x*x - y*y;
			if (zz>0.0)
				z = sqrt(zz);
			else
				z = 0;

			z *= (128.0f / (width / 2.0f));
			if (z<0) z = 0;
			if (z>255) z = 255;

			Cvector v;
			v.x = x;
			v.y = y;
			v.z = z;

			v.normalize();

			int alpha = 0;

			v.x = v.x * 127 + 127;
			v.y = v.y * 127 + 127;
			v.z *= 127;

			if (zz>=r)
				alpha = 255;
			else if (zz>=0.0)
				alpha = (int)(zz * (255.0 / r));

			int ix = (int)v.x;
			int iy = (int)v.y;
			int iz = (int)v.z;

			data[i++] = (alpha<<24) | (iz<<16) | (iy<<8) | ix;
		}
	}
}

float GetDistance(point2D a, point2D b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

const int voronoi_points_num = 128;
point2D vp[voronoi_points_num];

void GenVoronoi(int width, int height, unsigned int* data)
{
    srand(6510);

    for (int i=0; i<voronoi_points_num; i++)
    {
        vp[i].x = (float)(rand() % width);
        vp[i].y = (float)(rand() % height);
    }

	for (int y=0; y<height; y++)
	{
		for (int x=0; x<width; x++)
		{
			point2D p;
			p.x = (float)x;
			p.y = (float)y;

            int min_i = 0;
            float min = GetDistance(p, vp[0]);
            for (int i=1; i<voronoi_points_num; i++)
            {
                float d = GetDistance(p, vp[i]);

                if (d < min)
                {
                    min = d;
                    min_i = i;
                }
            }

            float cd = GetDistance(p, vp[min_i]);
			int c = (int)(cd * 1.0f);
			if (c > 255) c = 255;
			if (c < 0) c = 0;

			int r = c>>2;
			int g = c>>1;
			int b = c>>0;

			int cc = (r<<16) | (g<<8) | b;
			data[(x>>1) + (y>>1) * width] = cc;
			data[width-1 - (x>>1) + (y>>1) * width] = cc;
			data[(x>>1) + (height-1 - (y>>1)) * width] = cc;
			data[width-1 - (x>>1) + (height-1 - (y>>1)) * width] = cc;
		}
	}
}

void GenPallette(int width, unsigned int* data)
{
	for (int i=0; i<width; i++)
	{
		float cf = (float)i / (float)width;
		unsigned char c = (int)(cf * 255);
		data[i] = (c<<24) | (c<<16) | (c<<8) | c;
	}
}

void GenSine(int width, unsigned int* data)
{
	for (int i=0; i<width; i++)
	{
		float sf = (float)i / (float)width;
		float ssf = (float)((cos((double)sf * 2.0*PI) + 1.0) / 2.0f);
		unsigned char c = (int)(ssf * 255.0);
		data[i] = c;
	}
}

void GenPolar(int width, int height, unsigned int *data)
{
	double xc, yc;
	int i = 0;
	for (int y=0; y<height; y++)
	{
		yc = (double)(y - height / 2);
		for (int x=0; x<width; x++)
		{
			xc = (double)(x - width / 2);
			unsigned char r = (unsigned char)(sqrt(xc*xc + yc*yc) * (180.0 / (double)(width/2.0)));
			unsigned short a = (unsigned short)((atan2(yc, xc) / (2.0 * PI)) * 65535.0);
			data[i++] = (a << 8) | r;
		}
	}
}

void GenBump(int width, int height, unsigned int *data)
{
	for (int i=0; i<width * height; i++)
	{
		data[i] = rand() & 255;
	}

	for (int j=0; j<2; j++)
	{
		for (int i=0; i<width*height; i++)
		{
			unsigned int c = (data[abs((i-1) % (width * height))] + data[abs((i+1) % (width * height))] + data[abs((i-width) % (width * height))] + data[abs((i+width) % (width * height))]) >> 2;
			data[i] = c;
		}
	}

	srand(Ticks());

	int rs = rand() & 7;
	int gs = rand() & 7;
	int bs = rand() & 7;

	for (int i=0; i<width*height; i++)
	{
		unsigned char r = data[i] >> rs;
		unsigned char g = data[i] >> gs;
		unsigned char b = data[i] >> bs;
		data[i] = (data[i] << 24) | (r << 16) | (g << 8) | b;
	}
}

void GenLight(int width, int height, unsigned int *data)
{
	double xc, yc;
	int i = 0;

	for (int y=0; y<height; y++)
	{
		yc = (double)(y - height / 2);
		for (int x=0; x<width; x++)
		{
			xc = (double)(x - width / 2);
			double r = (xc*xc + yc*yc);
			unsigned int c = 255;
			if (r!=0)
				c = (int)(1.0 * 262144.0 / r);

			if (c > 255)
				c = 255;

			data[i++] = c<<24;
		}
	}
}

float LandscapePerlin(int x, int y, float d)
{
	float xp = (float)x * d;
	float yp = (float)y * d;

	float c = sin(xp / 75.0f) * 0.25f + sin(yp / 96.0f) * 0.2f + sin((xp + yp) / 128.0f) * 0.35f + 0.8f;
	return c / d;
}

void GenLandscape(int width, int height, unsigned int *data)
{
	int i = 0;
	float c;
	for (int y=0; y<height; y++)
	{
		for (int x=0; x<width; x++)
		{
			c = 0;
			for (int j=1; j<=4; j++)
				c += LandscapePerlin(x, y, (float)j);

			int cc = (int)(fmod((double)(c * 128.0f), 256.0));
			if (cc > 127) cc = 255 - cc;
			cc = cc * 2;
			data[i++] = (cc<<24) | (cc<<16) | (cc<<8) | cc;
		}
	}
}

void FilterBlursum(int width, int height, unsigned int *data)
{
	unsigned int *sumdata;
	sumdata = new unsigned int[width * height];

	for (int y=0; y<height; y++)
	{
		int sum = 0;
		for (int x=0; x<width; x++)
		{
			for (int j=0; j<=y; j++)
			{
				unsigned int c = data[j * width + x];
				unsigned int grad = ((((c>>16) & 255) * 80) >> 8) + ((((c>>8) & 255) * 128) >> 8) + (((c & 255) * 48) >> 8);
				sum += grad;
			}
			sumdata[y * width + x] = sum;
		}
	}

	for (int i=0; i<width * height; i++)
		data[i] = sumdata[i];

/*	unsigned int max = 0;
	for (int i=0; i<width * height; i++)
		if (max < sumdata[i])
			max = sumdata[i];

	for (int i=0; i<width * height; i++)
	{
		unsigned char c = (unsigned char)((sumdata[i] * 255) / max);
		data[i] = (255 << 24) | (c << 16) | (c << 8) | c;
	}*/
}

void FilterTexture(int width, int height, unsigned int *data, int filter)
{
	switch(filter)
	{
		case TFILTER_BLURSUM:
			FilterBlursum(width, height, data);
		break;

		default:
		break;
	}
}

GLuint GenTexture(int width, int height, unsigned int *src)
{
	GLuint txtnumber;
	unsigned int* data;

	data = new unsigned int[width * height];
	for (int i=0; i<width*height; i++)
	{
		*(data+i) = *(src+i);
	}

	glGenTextures(1, &txtnumber);

	glActiveTextureARB(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, txtnumber);
	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_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0,	GL_RGBA, GL_UNSIGNED_BYTE, data);

	delete [] data;

	return txtnumber;
}

GLuint GenTexture(int width, int height, int way, int dim)
{
	return GenTexture(width, height, way, dim, TFILTER_NULL);
}

GLuint GenTexture(int width, int height, int way, int dim, int filter)
{
	GLuint txtnumber;
	unsigned int* data;

	if (dim < 2 || height < 1) height = 1;
	if (width < 1) width = 1;
	data = new unsigned int[width * height];

	switch(way)
	{
		case TGEN_EMPTY:
			ZeroMemory(data, (width * height) * sizeof(unsigned int));
		break;

		case TGEN_BALL:
			GenTextureBall(width, height, data);
		break;

		case TGEN_VORONOI:
			GenVoronoi(width, height, data);
		break;

		case TGEN_PALLETTE:
			GenPallette(width, data);
		break;

		case TGEN_SINE:
			GenSine(width, data);
		break;

		case TGEN_POLAR:
			GenPolar(width, height, data);
		break;

		case TGEN_BUMP:
			GenBump(width, height, data);
		break;

		case TGEN_LIGHT:
			GenLight(width, height, data);
		break;

		case TGEN_LANDSCAPE:
			GenLandscape(width, height, data);
		break;

		default:
			ZeroMemory(data, (width * height) * sizeof(unsigned int));
		break;
	}

	if (filter!=TFILTER_NULL)
		FilterTexture(width, height, data, filter);

	glGenTextures(1, &txtnumber);

	switch(dim)
	{
		case 1:
			glActiveTextureARB(GL_TEXTURE0);
			glEnable(GL_TEXTURE_1D);
			glBindTexture(GL_TEXTURE_1D, txtnumber);
			glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			glTexImage1D(GL_TEXTURE_1D, 0, 4, width, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		break;

		case 2:
			glActiveTextureARB(GL_TEXTURE0);
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, txtnumber);
			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_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0,	GL_RGBA, GL_UNSIGNED_BYTE, data);
		break;
	}

	delete [] data;
	return txtnumber;
}

void IsoApply(unsigned int *cols, int iso, int size)
{
	for (int i=0; i<size; i++)
	{
		if (i < iso)
			cols[i] = 0;
	}
}

GLuint GenPalletteTexture(int size, unsigned int *cols, int iso)
{
	GLuint txtnumber;
	unsigned int* data;
	data = new unsigned int[size];

	for (int i=0; i<size; i++)
	{
		data[i] = cols[i];
	}

	IsoApply(data, iso, size);

	glGenTextures(1, &txtnumber);

	glActiveTextureARB(GL_TEXTURE1);
	glEnable(GL_TEXTURE_1D);
	glBindTexture(GL_TEXTURE_1D, txtnumber);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexImage1D(GL_TEXTURE_1D, 0, 4, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

	delete [] data;
	return txtnumber;
}
