#include <ctime>
#include "geometry.h"
#include "grid.h"
#include "GL/glut.h"





void Cell::draw() {
	glVertex3d(c[0],c[1],c[2]);
}



// we start the fluid above the bottom surface
// so it drops at the start of the simulation
Grid::Grid() 
{


	// define box which contains a space w x 3h x d 
	// 

	int left=0;
	int right=w+2;
	int back=0;
	int front=d+2;
	int bottom=0;
	int top=3*h+2;

	grid.resize(size);

	// first create the walls
	// top and bottom
	for (int x=left;x<=right;x++)
		for (int z=back;z<=front;z++) {
			createCell(x,bottom,z, SOLID);
			createCell(x,top,z, SOLID);
		}


	// left and right
	for (int y=bottom;y<=top;y++)
		for (int z=back; z<=front; z++) {
			createCell(left,y,z,SOLID);
			createCell(right,y,z,SOLID);
		}

	// front and back
	for (int x=left; x<=right; x++)
		for (int y=bottom; y<=top; y++) {
			createCell(x,y,back,SOLID);
			createCell(x,y,front,SOLID);
		}
	
	// now create the fluid cells
	for (int x=left+1; x<right; x++)
		for (int y=top-1-h;y<top; y++)
			for (int z=back+1;z<front;z++) {
				createCell(x,y,z,FLUID);
			}

				

}

void Grid::createCell(int x, int y, int z, int cType) {
		Cell* theCell= new Cell();
		theCell->setCenter(x,y,z);
		theCell->setType(cType);
		theCell->setVelocity(Tuple(0,0,0));
		grid[hash(x,y,z)].push_back(theCell);
}


 void Grid::update() {
	// compute time step
	double dt = kCFL * cSize/uMax;

	//update the grid  IMPLEMENT THIS

	// advance velocity
	advect(dt);

	// move particles

}

 void Grid::advect(double dt) 
 {

	 // advance velocities
	 for (int i=0;i<size;i++) {
		 for (vector<cellPtr>::iterator cell=grid[i].begin(); cell!=grid[i].end(); ++cell) {
			

			 // trace particle in current cell to its previous position
			

			 // store velocity of traced particle in temp velocity field
		

			 // apply external forces

			 // calculate pressure

			 // apply pressure

			 // extrapolate fluid

			 // set solid cell velocities
			

		 }
	 }


 }

 Tuple Grid::getVelocity(Tuple p)
 {
	 double vx = interpolateVelocity(p,0);
	 double vy = interpolateVelocity(p,1);
	 double vz = interpolateVelocity(p,2);
	 return Tuple(vx,vy,vz);
 }

 double Grid::interpolateVelocity(Tuple p, int index)
 {
	 int i = floor(p[0]);
	 int j = floor(p[1]);
	 int k = floor(p[2]);
	 double x=p[0];
	 double y=p[1];
	 double z=p[2];

	 double v=0;
	 v += (i+1-x) * (j+1-y) * (k+1-z) * getCellVelocity(i, j, k, index);
	 v += (x-i) * (j+1-y) * (k+1-z) * getCellVelocity(i+1, j, k, index);
	 v += (i+1-x) * (y-j) * (k+1-z) * getCellVelocity(i, j+1, k, index);
	 v += (x-i) * (y-j) * (k+1-z) * getCellVelocity(i+1, j+1, k, index);
	 v += (i+1-x) * (j+1-y) * (z-k) * getCellVelocity(i, j, k+1, index);
	 v += (x-i) * (j+1-y) * (z-k) * getCellVelocity(i+1, j, k+1, index);
	 v += (i+1-x) * (y-j) * (z-k) * getCellVelocity(i, j+1, k+1, index);
	 v += (x-i) * (y-j) * (z-k) * getCellVelocity(i+1, j+1, k+1,index);

	 return v;
 }

 double Grid::getCellVelocity(int x, int y, int z, int index)
 {
	 int hIndex = hash(x,y,z);

	 vector<cellPtr>::iterator cell=grid[hIndex].begin(); 
	 bool found=false;
	 Tuple v(0,0,0);
	 while (cell!=grid[hIndex].end() && found==false) 
	 {
		 if ((*cell)->getX() == x && (*cell)->getY() ==y && (*cell)->getZ() ==z) {
			 v=(*cell)->getVelocity();
			 found=true;
		 }
	 }
	 return v[index];
			 
 }




 void Grid::draw() {
	 glColor3f(1,0,0);
	 glBegin(GL_POINTS);
	 for (int i=0;i<size;i++) {
		 	for (vector<cellPtr>::iterator v=grid[i].begin(); v!=grid[i].end(); ++v)
				if ((*v)->getType()==FLUID) 
					(*v)->draw();
				//else {
				//	glColor3f(0,1,1);
				//	(*v)->draw();
				//	glColor3f(1,0,0);
				//}
	 }
	glEnd();
 }

 void Grid::reInit() {
 }