/*
 * =====================================================================================
 *
 *       Filename:  main.cpp
 *
 *    Description:  maim file containing main loop and freeglut support
 *
 *        Created:  12.06.2010 19:57:59
 *
 *         Author:  Łukasz Stanisławski (), lukasz.stanislawski@gmail.com
 *
 * =====================================================================================
 */

#include	"freeglut/freeglut.h"
#include	<GL/gl.h>
#include	"stdlib.h"

//mysz
int ref_x = 0;
int ref_y = 0;

//kamera
bool camera_on = false;

//wymiary swiata - tylko parzyste wartosci
const int length=100;
const int width=100;
const int heigth = 100;

unsigned char world[length][width][heigth];

const unsigned char empty = 0;
const unsigned char sand = 1;
const unsigned char metal = 2;
const unsigned char glass = 3;

static int deley = 0;

void drawWorld()
{
	float di,dj,dk;
	di = 1.0f/1.0f/(float)length;
	dj = 1.0f/1.0f/(float)width;
	dk = 1.0f/1.0f/(float)heigth;
	
		  
	for(int i=0;i<length;i++) 
		for(int j=0;j<width;j++) 
			for(int k=0;k<heigth;k++) {
				if(world[i][j][k] == empty)
					continue;
				if(world[i][j][k] == sand) {
					glPointSize(11);
					glBegin(GL_POINTS);
					glColor3f(0,0,0);
					glVertex3f(i*di-0.5f,j*dj-0.5f,k*dk);
					glEnd();

					glPointSize(9);
					glBegin(GL_POINTS);
					glColor3f(0.7f,0.3f,0.1f);
					glVertex3f(i*di-0.5f,j*dj-0.5f,k*dk);
					glEnd();

					/*glPointSize(10);
					glVertex3f(i*di-0.5,j*dj-0.5,k*dk);*/

					continue;
				}
				if(world[i][j][k] == metal) {
					glPointSize(11);
					glBegin(GL_POINTS);
					glColor3f(0,0,0);
					glVertex3f(i*di-0.5f,j*dj-0.5f,k*dk);
					glEnd();

					glPointSize(9);
					glBegin(GL_POINTS);
					glColor3f(1.0f,0.0f,1.0f);
					glVertex3f(i*di-0.5f,j*dj-0.5f,k*dk);
					glEnd();

					/*glPointSize(10);
					glVertex3f(i*di-0.5,j*dj-0.5,k*dk);*/
					continue;
				}
			}
}
void sandGenerator(int max)
{
	for(int i=0;i<max;i++)
		for(int j=0;j<max;j++) 
			world[length/2+i][width/2+j][heigth-1] = 1;
}
void sandEater(int max)
{
	for(int i=0;i<max;i++)
		for(int j=0;j<max;j++) 
			world[length/2-10+i][width/2-15+j][0] = empty;
}
void GenerateWall(int a,int x, int y, int z, int material)
{
	for(int i=0;i<=a;i++) {
		world[x+i][y][z]=material;
		world[x][y+i][z]=material;
		world[x+a][y+i][z]=material;
		world[x+i][y+a][z]=material;
	}
}
void GenerateBucket(int a,int x,int y,int z, int h)
{
	for(int i=0;i<h;i++)
		GenerateWall(a,x,y,z+i,2);
}
void GenerateClepsidre(int a,int x,int y,int z, int h,int c)
{
	for(int i=0;i<c;i++)
		GenerateBucket(a+2*i,x-i,y-i,z+i*(h-1),h);
//	for(int i=c;i>0;i++)
//		GenerateBucket(a-2*i,x+i,y+i,z+i*(h-1),h);
}
void generateSurface(int a,int b,int x,int y,int z)
{
	for(int i=0;i<a;i++)
		for(int j=0;j<b;j++)
			world[x+i][y+j][z]=metal;
}
void generateSlide(int width, int stepLen, int stepCount, int x,int y,int z)
{
	for(int i=0;i<stepCount;i++)
		generateSurface(width,stepLen,x,y+i*(stepLen-1),z+i);
}
void generateSlide2(int width, int stepLen, int stepCount, int x,int y,int z)
{
	for(int i=0;i<stepCount;i++)
		generateSurface(stepLen,width,x+i*(stepLen-1),y,z+i);
}
void processWorld2()
{
	int ran,ran_od;

	int wsp[8][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,1},{1,0},{1,-1}};

	for(int i=1;i<length-1;i++)
		for(int j=1;j<width-1;j++)
			for(int k=1;k<heigth;k++) {
				if(world[i][j][k]==sand) {
					if(world[i][j][k-1]==empty) {
						world[i][j][k]=empty;
						world[i][j][k-1]=sand;
						continue;
					}
					ran_od=rand()%8;
					ran=ran_od + 1;
					if(ran == 8) ran=0;
					while(ran != ran_od) {
						if(world[i+wsp[ran][0]][j+wsp[ran][1]][k-1] == empty) {
							world[i+wsp[ran][0]][j+wsp[ran][1]][k-1]=sand;
							world[i][j][k]=empty;
							break;
						}
						ran++;
						if(ran==8) ran=0;
					}
				}
			}

}
void processWorld(int d1,int d2,int d3)
{
	unsigned char *source[4];
	unsigned char *target[4];
	
	int r,h;

	for(int i=d1;i<length-d1;i+=2) 
		for(int j=d2;j<width-d2;j+=2)
			for(int k=d3;k<heigth-d3;k+=2) {

				for(int t=0;t<4;t++) {
					source[t]=NULL;
					target[t]=NULL;
				}

				//okreslenie skąd dokąd mogę się przemieszczać ziarenka
				if(world[i][j][k+1]==sand) source[0]=&world[i][j][k+1];
				if(world[i+1][j][k+1]==sand) source[1]=&world[i+1][j][k+1];
				if(world[i+1][j+1][k+1]==sand) source[2]=&world[i+1][j+1][k+1];
				if(world[i][j+1][k+1]==sand) source[3]=&world[i][j+1][k+1];	

				if(world[i][j][k]==empty) target[0]=&world[i][j][k];
				if(world[i+1][j][k]==empty) target[1]=&world[i+1][j][k];
				if(world[i+1][j+1][k]==empty) target[2]=&world[i+1][j+1][k];
				if(world[i][j+1][k]==empty) target[3]=&world[i][j+1][k];
				
				//"upuszczenie" piasku
				for(int l=0;l<4;l++) {
					if(source[l]!=NULL && target[l]!=NULL) {
						*source[l]=empty;
						*target[l]=sand;

						target[l]=NULL;
						source[l]=NULL;
					}
				}
				//ześlizgiwanie się piasku (albo nie ma targetów albo sourca)
				while((target[1]!=NULL || target[2] !=NULL || target[3]!=NULL || target[0]!=NULL) &&
						(source[1]!=NULL || source[2]!=NULL || source[3]!=NULL || source[0]!=NULL)) {
					r=rand()%4;
					while(source[r]==NULL) if(++r>3) r=0;
					h=rand()%4;
					while(target[h]==NULL) if(++h>3) h=0;

					*source[r]=empty;
					*target[h]=sand;
					source[r]=NULL;
					target[h]=NULL;
				}
			}
}
void renderScene(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //czyści tło	
//	glLoadIdentity();

	glColor3f(0,1,0);
 	glBegin(GL_QUADS);
  		glVertex3f(-0.5,-0.5,0.0);
  		glVertex3f(-0.5,0.5,0.0);
  		glVertex3f(0.5,0.5,0.0);
  		glVertex3f(0.5,-0.5,0.0);
  	glEnd();


//	processWorld(0,0,0);
//	processWorld(1,1,1);
//	processWorld(1,1,0);
//	processWorld(1,0,0);
//	processWorld(0,1,1);
//	processWorld(0,0,1);

//	processWorld(deley,deley,deley);
//	if(deley==0) deley=1;
//	else deley=0;

	processWorld2();
	drawWorld();

	glutSwapBuffers();
}	

void init() {
	
	glMatrixMode(GL_PROJECTION);
	gluPerspective(60,(float)640.0f/(float)480.0f,0.1,10);
	glMatrixMode(GL_MODELVIEW);
	gluLookAt(0,-1,0.75,0,0,0,0,1,0);

	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

//	generateSurface(15,20,length/2,width/2-20,10);
	GenerateBucket(20,length/2-20,width/2-20,0,20);
//	GenerateClepsidre(2,length/2-3,width/2-3,30,4,8);
	generateSlide(20,2,8,length/2-10,width/2-4,50);
	generateSlide2(20,2,16,length/2-5,width/2-20,30);
}
void mmouse(int x,int y)
{
	int dif_x,dif_y ;
	dif_x = ref_x - x;
	dif_y = ref_y - y;

	if(camera_on=true) {
		glRotatef(-dif_x*0.2f,0,0,1);
		glRotatef(-dif_y*0.1f,1,0,0);
		}
	ref_x = x;
	ref_y = y;
}
void mouse(int button, int state, int x, int y)
{
	switch(button) {
		case GLUT_RIGHT_BUTTON:
			if( state == GLUT_DOWN) camera_on=true;
			else camera_on = false;
			break;
	}
	ref_x = x;
	ref_y = y;
}
void keys(unsigned char key, int x, int y)
{
	if( key == 'a' || key=='A') 
		sandGenerator(3);
	if( key=='z' || key =='Z')
		glScalef(1.05f,1.05f,1.05f);
	if( key== 'x'|| key =='X')
		glScalef(0.95f,0.95f,0.95f);
	if( key=='s' || key =='S')
		glTranslatef(0,0,0.05f);	
	if( key=='w' || key =='W')
		glTranslatef(0,0,-0.05f);
	if( key=='q')
	sandEater(5);


}
void idle_func(void )
{
	glutPostRedisplay();
}
int main(int argc, char **argv) {

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);// | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(640,480);
	glutCreateWindow("Piasek 3d (A - syp piasek, Q - usun piasek, W,S - góra/dół, Z,X - bliżej/dalej");
	glutDisplayFunc(renderScene);
	init();
	//myszka
	glutMouseFunc(mouse);
	glutMotionFunc(mmouse);
	//klawirka
	glutKeyboardFunc(keys);

	glutIdleFunc(idle_func);
	glutMainLoop();
}
