#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

/*  Windows only!! */
/*#define GLUT_STATIC 1 */
#include <gl/glut.h>

#define NUM_COMPONENTS 4
#define min(a,b)  ((a)<(b) ? (a) : (b))
#define max(a,b)  ((a)>(b) ? (a) : (b))

/* Global variables */

/* pointer to the array of pixels, 4 bytes RGBA per pixel */
static unsigned char* Checkerboard = 0;

static int CheckerWidth = 0, CheckerHeight = 0, CheckerStep = 0;

/* current position of the bitmap */
static int CheckerPosX =0, CheckerPosY = 0;

/* indicates whether we move the bitmap diagonally as a result of mouse
  clicks (1) or draw on the bitmap (0) */
static int CheckerMove = 1;

unsigned char* MakeChecker( int width, int height, int step )  {
  unsigned char* bitmap;
  int i,j;
  bitmap = (unsigned char*)malloc( width*height*NUM_COMPONENTS);

  for( j = 0; j < height; j++) {
    for( i = 0; i < width; i++) {
      /* for each pixel choose a color; if  the pixel is in a step X step square with vertical and horizontal number
          both even or odd, then we make it white, otherwise black
      */
      if( (i % (2*step) < step)  == (j % (2*step) < step) ) {
        /* each pixel has 4 bytes: RGB and A (alpha) which we do not use */
        bitmap[(j*width+i)*NUM_COMPONENTS]   = 255;
        bitmap[(j*width+i)*NUM_COMPONENTS+1] = 255;
        bitmap[(j*width+i)*NUM_COMPONENTS+2] = 255;
        bitmap[(j*width+i)*NUM_COMPONENTS+3] = 0;
      } else {
        bitmap[(j*width+i)*NUM_COMPONENTS] =   0;
        bitmap[(j*width+i)*NUM_COMPONENTS+1] = 0;
        bitmap[(j*width+i)*NUM_COMPONENTS+2] = 0;
        bitmap[(j*width+i)*NUM_COMPONENTS+3] = 0;
      }
    }
  }
  return bitmap;
}

/* magic function to setup the mapping between virtual space
    and screen; we will learn about this later */
void Reshape(int width, int height) {
  glViewport(0,0,width,height);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0,width, 0,height);
}

/* callback for button clicks; gets called each time amouse butten is pressed or released */
void Mouse( int button, int state, int x, int y) {
  if( button == GLUT_LEFT_BUTTON ) {
    if( state == GLUT_UP ) {
      if(CheckerMove) {
        /* shift  the position up and right */
        /* in a "real" program we should check that we stay within the bounds of the window */
        CheckerPosX = CheckerPosX+1;
        CheckerPosY = CheckerPosY+1;
        /* puts a display event on the event queue to indicate that
           the window has to be redrawn because the bitmap postion has changed */
        glutPostRedisplay();
      }
    } else if (state == GLUT_DOWN) {

    } else {
      assert(0);
    }
  } else if( button == GLUT_MIDDLE_BUTTON ) {

  } else if( button == GLUT_RIGHT_BUTTON) {
    if( state == GLUT_UP ) {
      if(CheckerMove) {
        /* shift  the position down and left */
        /* in a "real" program we should check that we stay within the bounds of the window */
        CheckerPosX = CheckerPosX-1;
        CheckerPosY = CheckerPosY-1;
        glutPostRedisplay();
      }

    } else if (state == GLUT_DOWN) {

    } else {
      assert(0);
    }

  } else {
     assert(0);
  }
}

/* callback  for keyboard keys*/
void Keyboard(unsigned char key, int x, int y ) {
 /* space bar toggles the current state of CheckerMove */
  if( key == ' ')
     CheckerMove = !CheckerMove;

}

/* callback for  motion of the mouse with a button pressed */

void Motion(int x, int y) {
  int pixnumX;
  int pixnumY;
  int pixnum;

  if( !CheckerMove ) {
   /* assign red = (255,0,0) to the pixel under the mouse */
    /* using min and max we make sure that the pixel is within the checkerboard
       otherwise we may end p writing outside our array! */
    pixnumX = min( CheckerWidth-1, max(0,x-CheckerPosX));
    /* for Y,we also need to flip the direction: bitmap rows are counted
      from the top, but pixels in the window from the bottom */
    pixnumY = min( CheckerHeight-1,max(0, (CheckerHeight-y)-CheckerPosY));
    pixnum = pixnumY*CheckerWidth+pixnumX;
    Checkerboard[pixnum*NUM_COMPONENTS]   = 255;
    Checkerboard[pixnum*NUM_COMPONENTS+1] = 0;
    Checkerboard[pixnum*NUM_COMPONENTS+2] = 0;
    Checkerboard[pixnum*NUM_COMPONENTS+3] = 0;
    glutPostRedisplay();
  }
}

/* window redraw even callback; is called when the window is created, maximized, resized etc.; also called if
glutPostRedisplay generates the necessary event  */
void Draw() {
  /* in the double buffer mode all drawing happens in the invisible back buffer */
  glClearColor( 1.0, 0.0,0.0,0.0);
  glClear(GL_COLOR_BUFFER_BIT);
  /* We can use pixel coordinates here because we have the right magic in Reshape */
  glRasterPos2i(CheckerPosX,CheckerPosY);
  glDrawPixels(CheckerWidth, CheckerHeight, GL_RGBA, GL_UNSIGNED_BYTE, Checkerboard);
  glFlush();
  /* once we are done   drawing we swap the back and front buffers */
  glutSwapBuffers();
}

int main(int argc, char* argv[]) {
   /* initialize glut and parse command-line aguments that glut understands */
   glutInit(&argc, argv);

   /* create the checkerboard */
   CheckerWidth = 256;
   CheckerHeight = 256;
   CheckerStep = 16;

   Checkerboard = MakeChecker(CheckerWidth, CheckerHeight, CheckerStep);
   /* initialize dislay mode: 4 color components, double buffer */
   glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);

   glutInitWindowSize(256,256);
   glutCreateWindow("a simple window ");

  /* register all callbacks */
   glutDisplayFunc(Draw);
   glutReshapeFunc(Reshape);
   glutMouseFunc(Mouse);
   glutKeyboardFunc(Keyboard);
   glutMotionFunc(Motion);

   /* this is an infinte loop get event - dispatch event which never returns */
   glutMainLoop();
   return 0;
}
