#include <GL/gl.h>
#include <GL/glut.h>
//http://www.cs.toronto.edu/~wongam/d18_cygwin_opengl_setup/cygwin_opengl_setup.html

#include <ctime>
#include <cerrno>
#include <csignal>
#include <cstring>
#include <cstdlib>

#include <iostream>

#include "GraphicsScene.h"

using namespace std;

void sigHandler( int signum ) {
  cout << "caught signal " << signum << endl;
}

void sleepFor(long nsecs ) {
  time_t seconds = nsecs / 1000000000;
  timespec sleepPeriod = { seconds, nsecs % 1000000000};
  timespec unusedPeriod;

  for (;;) {
    if (nanosleep( &sleepPeriod, &unusedPeriod) == 0) {
      break;
    }
    else if (errno == EINTR) {
      cout << "sig detected... resuming timer." << endl;
      sleepPeriod = unusedPeriod;
    }
    else {
      cerr << "Error occurred with nanosleep; reason = " << strerror(errno)
          << endl;
      break;
    }
  }
}

void mouse(int button, int state, int x, int y) {
  switch (button) {
    case GLUT_LEFT_BUTTON:
      cout << "user clicked the left mouse button" << endl;
      break;
    case GLUT_RIGHT_BUTTON:
      cout << "user clicked the right mouse button" << endl;
      break;
    default:
      break;
  }
}

void keyboard (unsigned char key, int x, int y) {
  switch (key) {
    case 'p':
      cout << "user pressed the p key" << endl;
      break;
  }
  
  glFlush();
}

void init (void) {
  /* select clearing color      */
  /* this color is white --- full red, green, and blue components */
  /* the last component is alpha(transparency) --- use zero */
  glClearColor(1.0, 1.0, 1.0, 0.0);
}

void reshape(int w, int h) {
  /* This routine is called when the initial GL window is created
     and when the window is resized.  It is followed in execution
     by the 'display' routine. */

  /* initialize viewing values  */
  glViewport(0, 0, (GLint) w - 1, (GLint) h - 1);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0, (GLdouble) w, 0.0, (GLdouble) h, -1.0, 1.0);
}

void drawElement(int size, int center_x, int center_y, double rotation) {
  /* draws a square of size, at center x,y */
  int half_size = size/2;
  
  glBegin(GL_POLYGON);
    glVertex2i (center_x - half_size, center_y - half_size);
    glVertex2i (center_x - half_size, center_y + half_size);

    glVertex2i (center_x + half_size, center_y + half_size);
    glVertex2i (center_x + half_size, center_y - half_size);
  glEnd(); 
}

void display(void) {
  /* clear all pixels  */
  glClear(GL_COLOR_BUFFER_BIT);
  
  /* fractal depth 1 */
  glColor3ub (255, 0, 0); /* red */
  drawElement(200, 200, 200, 0);
  
  /* fractal depth 2 */
  glColor3ub (0, 255, 0); /* green */
  drawElement(100, 100, 200, 0);
  drawElement(100, 300, 200, 0);

  /* fractal depth 3 */
  glColor3ub (0, 0, 255); /* blue */
  drawElement(50, 100, 150, 0);
  drawElement(50, 100, 250, 0);
  drawElement(50, 300, 150, 0);
  drawElement(50, 300, 250, 0);

  /* fractal depth 4 */
  glColor3ub (255, 255, 0); /* yellow */
  drawElement(25, 75, 150, 0);
  drawElement(25, 75, 250, 0);
  drawElement(25, 125, 150, 0);
  drawElement(25, 125, 250, 0);
  drawElement(25, 275, 150, 0);
  drawElement(25, 275, 250, 0);
  drawElement(25, 325, 150, 0);
  drawElement(25, 325, 250, 0);

  SCENE.step++;
   
  /* don't wait!  
   * start processing buffered OpenGL routines 
   */
  glFlush();
}

void update(void) {
  // sleep for one second
  sleepFor(1000000000);
  
  glutPostRedisplay();
}


int main(int argc, char **argv) {
  signal(SIGUSR2, sigHandler);
  
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(400, 400); 
  glutInitWindowPosition(400, 350);
  glutCreateWindow("Fractal Generator");

  init();

  glutDisplayFunc(display); 
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keyboard);
  glutIdleFunc(update);
  
  glutMainLoop();

  return 0;
}
