/****************************************************************************************************
* Influence map example using convolution with Gaussian filter to spread out influence.
#define GRIDSIZE is used to set map size - here its 32 by 32.
Press space bar to trigger a pass of the filter over the map.
Some messy techniques here! - be careful.
****************************************************************************************************/


////// Includes
#include <stdio.h>
#include <stdlib.h>
#include "glut.h"
#define GRIDSIZE 32


////// Global Variables
typedef struct rgbtag{
	float r;
	float g;
	float b;
}rgb;


rgb grid[GRIDSIZE*GRIDSIZE];
rgb gridtemp[GRIDSIZE*GRIDSIZE];
//function prototypes
void init(void);
void display(void);
void drawBox(float w, float h, float l);
void reshape(int w, int h);

void init()
// Initialize
//************************************************************************
// desc: initializes OpenGL
{
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);		// clears colour buffer 
	glEnable(GL_DEPTH_TEST);					// hidden surface removal
	glEnable(GL_CULL_FACE);						// do not calculate inside of poly's
	glFrontFace(GL_CCW);						// counter clock-wise polygons are out

    for (int i=0;i<GRIDSIZE;i++){
	  for (int j=0;j<GRIDSIZE;j++){
		  
		  grid[i+(j*GRIDSIZE)].r=0;
		  grid[i+(j*GRIDSIZE)].g=0;
		 grid[i+(j*GRIDSIZE)].b=0;
 	  }
	}
}

void drawBox(float w, float h, float l)
//drawBox
//***************************************************************************
//This function first translates a further x,y,z units then draws a box of
//width w, height h, length l
{
	    glPushMatrix();       //save modelview 
		w=w/2.0; h = h/2.0; l = l/2.0; //adjust values so centre is in middle of box
		//draw faces of box
		glBegin(GL_POLYGON);
		   glVertex3f(w, l, h);	
	       glVertex3f(w, l, -h);
	       glVertex3f(-w, l, -h);
		   glVertex3f(-w, l, h);
		glEnd();
		glBegin(GL_POLYGON);
			glVertex3f(w, l, h);	
	        glVertex3f(-w, l, h);
	        glVertex3f(-w, -l, h);
            glVertex3f(w, -l, h);
		glEnd();
		glBegin(GL_POLYGON);
			glVertex3f(w, l, h);	
			glVertex3f(w, -l, h);
			glVertex3f(w, -l, -h);
			glVertex3f(w, l, -h);
		glEnd();
		glBegin(GL_POLYGON);
		    glVertex3f(-w, l, h);	
			glVertex3f(-w, l, -h);
			glVertex3f(-w, -l, -h);
			glVertex3f(-w, -l, h);
		glEnd();
		glBegin(GL_POLYGON);
			glVertex3f(-w, -l, h);
			glVertex3f(-w, -l, -h);
			glVertex3f(w, -l, -h);
			glVertex3f(w, -l, h);	
		glEnd();
		glBegin(GL_POLYGON);
			glVertex3f(w, -l, -h);
			glVertex3f(-w, -l, -h);
			glVertex3f(-w, l, -h);
			glVertex3f(w, l, -h);	
		glEnd();
	glPopMatrix();   //restore previous modelview matrix so leaving things as you found them 
}


// display
//************************************************************************
// desc: handles drawing of scene
void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// clear screen and depth buffer
	glLoadIdentity();										// reset modelview matrix


	gluLookAt(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); //reset camera position
    glColor3f(0,0,0);  //set colour
    glTranslatef(0.5,0.5,0.5);  //move by x,y and z units
	//tests grid by applying greyscale values to each position
    //
	for (int i=0;i<GRIDSIZE;i++){
	  for (int j=0;j<GRIDSIZE;j++){
		  if( grid[i+(j*GRIDSIZE)].r>grid[i+(j*GRIDSIZE)].b)
		  {
		  glColor3f(grid[i+(j*GRIDSIZE)].r,0,0);
		  }
		  else
          {
		  glColor3f(0,0,grid[i+(j*GRIDSIZE)].b);  //set colour		  }
		  }
          glPushMatrix(); //save current position/orientation, etc.
	      glTranslatef(i,j,0);
	      drawBox(1,1,1);  //draw 
 	      glPopMatrix(); //restore previous "current" position/orientation etc.
	  }
	}

	
	glFlush();  //force drawing

	glutSwapBuffers();			// bring backbuffer to foreground
    //any errors then display error codes 
	GLenum errCode;
    const GLubyte *errString;
    if((errCode = glGetError()) != GL_NO_ERROR) 
    {
	   errString = gluErrorString(errCode);
	   fprintf (stderr,"OpenGL error : %s\n",errString);
    }

}
void keyboardread(unsigned char key, int x,int y)
{
	switch (key) {
	case 'u':
			
			break;
	case ' ':
			
			for (int i=1;i<GRIDSIZE-1;i++){
	          for (int j=1;j<GRIDSIZE-1;j++){
				gridtemp[i+(j*GRIDSIZE)].r = grid[i+1+((j-1)*GRIDSIZE)].r*(1.0/16.0) +
										   grid[i+1+((j*GRIDSIZE))].r*(2.0/16.0) +
										   grid[i+1+((j+1)*GRIDSIZE)].r*(1.0/16.0)+
				  						   grid[i+((j-1)*GRIDSIZE)].r*(2.0/16.0) +
										   grid[i+((j*GRIDSIZE))].r*(4.0/16.0) +
										   grid[i+((j+1)*GRIDSIZE)].r*(2.0/16.0)+
										   grid[i-1+((j-1)*GRIDSIZE)].r*(1.0/16.0) +
	 									   grid[i-1+(j*GRIDSIZE)].r*(2.0/16.0) +
  										   grid[i-1+((j+1)*GRIDSIZE)].r*(1.0/16.0);

				gridtemp[i+(j*GRIDSIZE)].b = grid[i+1+((j-1)*GRIDSIZE)].b*(1.0/16.0) +
										   grid[i+1+((j*GRIDSIZE))].b*(2.0/16.0) +
										   grid[i+1+((j+1)*GRIDSIZE)].b*(1.0/16.0)+
				  						   grid[i+((j-1)*GRIDSIZE)].b*(2.0/16.0) +
										   grid[i+((j*GRIDSIZE))].b*(4.0/16.0) +
										   grid[i+((j+1)*GRIDSIZE)].b*(2.0/16.0)+
										   grid[i-1+((j-1)*GRIDSIZE)].b*(1.0/16.0) +
	 									   grid[i-1+(j*GRIDSIZE)].b*(2.0/16.0) +
  										   grid[i-1+((j+1)*GRIDSIZE)].b*(1.0/16.0);


  			  } //convolution calc.  very messy tidy up using matrix mult.
			}
			   for (int i=0;i<GRIDSIZE*GRIDSIZE;i++) //copy calculated array into original
				      grid[i]=gridtemp[i];
			 //quicker to swap pointers to arrays - kinda double buffering  
			  //2 influence points set here
			   grid[GRIDSIZE/2+(GRIDSIZE*GRIDSIZE/2)].r=1;  //use this if you want to keep inf at level
							//1 in original unit else unit's influence value is decreased at each pass.
               grid[GRIDSIZE/2 - 5 +(GRIDSIZE*(GRIDSIZE/2 + 1))].b=1;

 			   glutPostRedisplay();
			break;
	}
}

void reshape(int w, int h)
//reshape
//************************************************************************
//OpenGL reshape callback is called when the window is resized
{
	  glViewport(0,0,(GLsizei) w, (GLsizei) h); //respecify size of view port to match new w and h
	  glMatrixMode(GL_PROJECTION);  //go into projection mode
	  glLoadIdentity();   //reset projection matrix
     glOrtho(0.0, GRIDSIZE, 0.0, GRIDSIZE, 0.0, 100.0); //defines orthoganal projection (parrallel
											// viewing volume
											// window limits of display
											// left =0.0, right =2.0,
											// bottom = 0.0, top = 2.0
											// near =0.0, far = 100.0
		  //gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,200.0);  //redefine projection matrix to match changes in w and h
	  glMatrixMode(GL_MODELVIEW);  //switch back to modelview mode
	  glLoadIdentity(); //reset modelview matrix
	  gluLookAt(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); //reset camera position
}

int main(int argc, char** argv)
{
	  glutInit(&argc,argv);
	  glutInitDisplayMode(GLUT_DOUBLE |GLUT_RGB | GLUT_DEPTH);
	  glutInitWindowSize(500,500);
	  glutInitWindowPosition(100,100);
	  glutCreateWindow(argv[0]);
	  init();
	  glutDisplayFunc(display);
	  glutReshapeFunc(reshape);
	  glutKeyboardFunc(keyboardread);
	  glutMainLoop();
	  return 0;
}



