#include <math.h>
#include <stdlib.h>
#include "project_volumes.h"
#include "project_textures.h"
#include "project_timer.h"
#include "fx_colors.h"

int fxw_vsize;
int fxw_vvsize;
int fxw_vvvsize;

#define water_max_energy 65535
#define water_energy_shift 8

int *wbuffer1, *wbuffer2;

unsigned int fxw_argb[256];

// ====================================

const int num_crafts = 4;
const int drop_frequency = 16;

float fxw_div1[num_crafts];
float fxw_div2[num_crafts];
float fxw_div3[num_crafts];
int fxw_mul1[num_crafts];
int fxw_mul2[num_crafts];
int fxw_mul3[num_crafts];

// ====================================

bool FxWaterInit = false;
bool FxWaterColorsInit = false;

void InitFxWaterColors()
{
	if (FxWaterColorsInit) return;

	SetPalette(fxw_argb, GetColor(0,0,0,0), GetColor(128,128,255,255), 0, 255);

	GenPalletteTexture(256, fxw_argb, 96);

	FxWaterColorsInit = true;
}

void InitFxWater(Cvolume *vol)
{
	fxw_vsize = vol->GetWidth();
	fxw_vvsize = fxw_vsize * fxw_vsize;
	fxw_vvvsize = fxw_vvsize * fxw_vsize;

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

    for (int i=0; i<fxw_vvvsize; i++)
    {
        wbuffer1[i] = 0;
        wbuffer2[i] = 0;
    }

	float slowness = 2.5;
	for (int i=0; i<num_crafts; i++)
	{
		fxw_div1[i] = (float)(512.0 + rand() % 1024) * slowness;
		fxw_div2[i] = (float)(512.0 + rand() % 1024) * slowness;
		fxw_div3[i] = (float)(512.0 + rand() % 1024) * slowness;

		fxw_mul1[i] = fxw_vsize / 4 + rand() % (int)(fxw_vsize/4);
		fxw_mul2[i] = fxw_vsize / 4 + rand() % (int)(fxw_vsize/4);
		fxw_mul3[i] = fxw_vsize / 4 + rand() % (int)(fxw_vsize/4);
	}

	InitFxWaterColors(); 

	FxWaterInit = true;
}

void FxDrawDrop(int px, int py, int pz, int size, int energy)
{
	int loop = (size + 1) / 2;
    for (int z=-loop; z<loop; z++)
    {
    	for (int y=-loop; y<loop; y++)
    	{
    		for (int x=-loop; x<loop; x++)
    		{
    			int xpp = x + px;
    			int ypp = y + py;
    			int zpp = z + pz;

    			if (xpp < 0) xpp = 0;
    			if (ypp < 0) ypp = 0;
    			if (zpp < 0) zpp = 0;
    			if (xpp > fxw_vsize - 1) xpp = fxw_vsize - 1;
        		if (ypp > fxw_vsize - 1) ypp = fxw_vsize - 1;
        		if (zpp > fxw_vsize - 1) zpp = fxw_vsize - 1;

    			int i = xpp + ypp * fxw_vsize + zpp * fxw_vvsize;
    			wbuffer1[i] = energy;
            }
		}
	}
}


void FxWaterDrops(int ticks)
{
	for (int i=0; i<num_crafts; i++)
	{
		int px = fxw_vsize/2 + (int)(sin((float)ticks / fxw_div1[i]) * fxw_mul1[i]);
		int py = fxw_vsize/2 + (int)(sin((float)ticks / fxw_div2[i]) * fxw_mul2[i]);
		int pz = fxw_vsize/2 + (int)(sin((float)ticks / fxw_div3[i]) * fxw_mul3[i]);

		FxDrawDrop(px, py, pz, 3, water_max_energy);
	}

	if (IsIterReady(drop_frequency))
	{
		int px = fxw_vsize/16 + rand() % (int)(fxw_vsize/1.125);
		int py = fxw_vsize/16 + rand() % (int)(fxw_vsize/1.125);
		int pz = fxw_vsize/16 + rand() % (int)(fxw_vsize/1.125);

		FxDrawDrop(px, py, pz, 4, water_max_energy);
	}
}


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

void FxRenderWaterBuffer(Cvolume *vol)
{
	unsigned char *voxels = vol->GetDataPointer();

    for (int i=0; i<fxw_vvvsize; i++)
    {
        voxels[i] = (unsigned char)(wbuffer1[i] >> water_energy_shift);
    }
}

void FxRunWater(Cvolume *vol, int ticks)
{
	if (!FxWaterInit) InitFxWater(vol);

	FxWaterDrops(ticks);

	for (int i = 1 + fxw_vsize + fxw_vvsize; i < fxw_vvvsize - fxw_vvsize - fxw_vsize - 1; i++)
	{
		wbuffer2[i] = ((wbuffer1[i-1] + wbuffer1[i+1] + wbuffer1[i-fxw_vsize] + wbuffer1[i+fxw_vsize] + 2*wbuffer1[i-fxw_vvsize] + 2*wbuffer1[i+fxw_vvsize]) >> 2)  - wbuffer2[i];
		wbuffer2[i] -= (wbuffer2[i] >> 8);

		if (wbuffer2[i] < 0 ) wbuffer2[i] = 0;
		if (wbuffer2[i] > water_max_energy) wbuffer2[i] = water_max_energy;
	}

	FxWaterSwapBuffers();
	FxRenderWaterBuffer(vol);
}
