#include <math.h>
#include <stdio.h>

#include "project_volumes.h"
#include "project_screen.h"
#include "math_vector3D.h"

Cvolume::Cvolume(int width, int height, int depth)
{
	this->width = width;
	this->height = height;
	this->depth = depth;

	this->iso_depth = default_iso_depth;

	this->data = new unsigned char[width * height * depth];
}

Cvolume::~Cvolume()
{
}

int Cvolume::GetWidth()
{
	return width;
}

int Cvolume::GetHeight()
{
	return height;
}

int Cvolume::GetDepth()
{
	return depth;
}

int Cvolume::GetIsoDepth()
{
	return iso_depth;
}

void Cvolume::SetIsoDepth(int iso_depth)
{
	this->iso_depth = iso_depth;
}

unsigned char* Cvolume::GetDataPointer()
{
	return data;
}

// Volume Generation Algorithms
// ----------------------------

#define fsin_size 65536
bool pvolume_init = false;
int fsin1[fsin_size], fsin2[fsin_size], fsin3[fsin_size];

void init_pvolume()
{
	for (int i=0; i<fsin_size; i++)
	{
		fsin1[i] = (int)(sin(i / 45.0) * 123.0);
		fsin2[i] = (int)(sin(i / 63.0) * 97.0);
		fsin3[i] = (int)(sin(i / 200.0) * 248.0);
	}

	pvolume_init = true;
}

int EvalSines(int x, int y, int z, int octave)
{
	return ((fsin1[x << octave] >> octave) + (fsin2[y << octave] >> octave) + (fsin3[z << octave] >> octave) + (fsin3[(x + y + 64) << octave] >> octave));
}

void ClearBorders(int width, int height, int depth, unsigned char *voldata)
{
	int wh = width * height;

	for (int y=0; y<height; y++)
	{
		for (int x=0; x<width; x++)
		{
			voldata[y * width + x] = 0;
			voldata[(depth-1) * wh + y * width + x] = 0;
		}
	}

	for (int z=0; z<depth; z++)
	{
		for (int y=0; y<height; y++)
		{
			voldata[z * wh + y * width] = 0;
			voldata[z * wh + y * width + (width-1)] = 0;
		}
	}

	for (int z=0; z<depth; z++)
	{
		for (int x=0; x<width; x++)
		{
			voldata[z * wh + x] = 0;
			voldata[z * wh + (height-1) * width + x] = 0;
		}
	}
}

void Cvolume::GenVolumePlasma()
{
	if (!pvolume_init) init_pvolume();

	int i = 0;
	for (int z=0; z<depth; z++)
	{
		float zz = (float)(depth - z) / (float)depth;
		for (int y=0; y<height; y++)
		{
			for (int x=0; x<width; x++)
			{
				int ii = 0;
				for (int j=1; j<=6; j++)
				{
					ii +=  EvalSines(x,y,z,j);
				}
				int cc = ii & 511;

				if (x==0 || x==width-1 || y==0 || y==height-1 || z==0 || z==depth-1) cc = 0;
				if (cc > 255) cc = 511 - cc;
				int aa = 255;
				if (cc < 192) cc = 0;

				data[i++] = cc;
			}
		}
		printf("%d / %d\r",z,depth);
	}
}

void Cvolume::GenVolumeSphere()
{
	printf("\n");

	int i = 0;
	for (int z=0; z<depth; z++)
	{
		float z_center = z - depth / 2.0f;
		for (int y=0; y<height; y++)
		{
			float y_center = y - height / 2.0f;
			for (int x=0; x<width; x++)
			{
				float x_center = x - width / 2.0f;
				float c = 2.0f * (width / 2.0f - sqrt(x_center * x_center + y_center * y_center + z_center * z_center));
				if (c < 0) c = 0;
				if (c > width) c = (float)width;

				float cc = c / (float)(width);

				unsigned char ci = (unsigned char)(255 * cc);

				data[i++] = ci;
			}
		}
		printf("%d / %d\r",z,depth);
	}
}

void Cvolume::FadeByDistanceFromCenter(float radius)
{
	printf("\n");

	int i = 0;
	for (int z=0; z<depth; z++)
	{
		float z_center = z - depth / 2.0f;
		for (int y=0; y<height; y++)
		{
			float y_center = y - height / 2.0f;
			for (int x=0; x<width; x++)
			{
				float x_center = x - width / 2.0f;
				float c = radius * (width / 2.0f - sqrt(x_center * x_center + y_center * y_center + z_center * z_center));
				if (c < 0) c = 0;
				if (c > width) c = (float)width;

				float cc = c / (float)width;

				unsigned char ccc = (unsigned char)((float)data[i] * cc);

				data[i++] = ccc;
			}
		}
		printf("%d / %d\r",z,depth);
	}
}

void Cvolume::ZeroMemory()
{
	for (int i=0; i<width * height * depth; i++)
	{
		data[i] = 0;
	}
}

void Cvolume::GenVolume(int way)
{
	switch(way)
	{
		case VGEN_EMPTY:
			ZeroMemory();
		break;

		case VGEN_PLASMA:
			GenVolumePlasma();
			FadeByDistanceFromCenter(16);
		break;

		case VGEN_SPHERE:
			GenVolumeSphere();
		break;

		default:
			ZeroMemory();
		break;
	}
}

// Volume Loading Functions
// ------------------------

void Cvolume::LoadVolume(char *filename)
{
	// load file

	unsigned short* fdata;
	FILE *fvol;
	fvol = fopen(filename,"rt");
	int count = 0;

	fseek(fvol, 0, SEEK_END);
    count = ftell(fvol) / 2;
    rewind(fvol);

	fdata = new unsigned short[count];
	fread(fdata, sizeof(char), count, fvol);
	fclose(fvol);


	// file loaded

	for (int i=0; i<count; i++)
	{
		int c = fdata[i] >> 6;
		int a = c >> 8;
		//if (a < 128) a = 0;
		unsigned char cc = c;
		data[i] = cc;
	}

	delete [] fdata;
}

// Volume Rendering Functions
// --------------------------

void Cvolume::RenderVolumeOpenCL(Cvector pos, Cvector dir, Cvector up, Cvector right)
{
}

void Cvolume::RenderVolume(int way, Cvector pos, Cvector dir, Cvector up, Cvector right)
{
	switch(way)
	{
		case VRND_OPENCL:
			RenderVolumeOpenCL(pos, dir, up, right);
		break;

		default:
			RenderVolumeOpenCL(pos, dir, up, right);
		break;
	}
}
