#include "GlutWindow2d.h"
#include <iostream>
using namespace std;
//********************************************************
/*! Basic constructor */
GlutWindow2d::GlutWindow2d(const char *title, int width, int height) : GlutWindow(title, width, height)
{
   //initialize the zoom factor to nothing
   zoomFactor = 1.0;

   //initialize the panning offsets
   panXOffset = 0;
   panYOffset = 0;
   
   drawZoomCircleFlag = false;
   zoomDrawRadius = 0;
}


//********************************************************
/*! Basic destructor */
GlutWindow2d::~GlutWindow2d()
{

}

//********************************************************
/*!
  This performs generic graphics matrix setup operations, and
  will be called if the user resizes the window
  @param w Width of the window
  @param h Height of the window
*/
void GlutWindow2d::reshape(int w, int h)
{
   //need to set the viewable area on the screen to the dimensions of the window 
   glViewport(0,0,w,h);

   //I am setting a state where I am editing the projection matrix...
   glMatrixMode(GL_PROJECTION);
   
   //Clearing the projection matrix...
   glLoadIdentity();
   
   //Creating an orthoscopic view matrix going from -1 -> 1 in each
   //dimension on the screen (x, y, z). 
   //void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);

   glOrtho(-w/2*zoomFactor + panXOffset, w/2*zoomFactor + panXOffset, -h/2*zoomFactor + panYOffset, h/2*zoomFactor + panYOffset, -1, 1);
   //glOrtho(-1, 1, -1, 1, -1, 1);

    
   //Now editing the model-view matrix.
   glMatrixMode(GL_MODELVIEW);

   //Clearing the model-view matrix.
   glLoadIdentity();
   
   windowWidth = w;
   windowHeight = h;
}

//********************************************************
/*!
  This will setup the camera and then call draw().  
*/
void GlutWindow2d::display()
{
   //Now editing the model-view matrix.
   //glMatrixMode(GL_MODELVIEW);
   
   //clear the screen to the clear color (default black)
   glClear(GL_COLOR_BUFFER_BIT);	
   
   //glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   
   //set the default object color to white
   glColor3f(1,1,1);
   
   glPushMatrix();
   
   //position the camera at the correct location
   positionCamera();

   //call the overridden function to draw the current scene
   draw();

   glPopMatrix();

   //force execution of the previous GL commands in finite time
   glFlush();

   glutSwapBuffers();
}

//********************************************************
/*!
  This function is called when an ascii key is pressed.
  @param key The key that was pressed
  @param x The x location of the cursor when the key was pressed
  @param y The y location of the cursor when the key was pressed
*/
void GlutWindow2d::processNormalKeys(unsigned char key,int x, int y)
{ 
   switch(key)
   {
   case 27: //esc
      exit(0);
      
      break;

   case 'r': //resets all of the zoom and pan parameters
      zoomFactor = 1.0;
      panXOffset = 0;
      panYOffset = 0;

      setProjectiveSettings();
      break;
      
   default:
      
      break;
   }

   GlutWindow::processNormalKeys(key, x, y);
}

//********************************************************
/*!
  This function is used to perform actions when one of the non-ascii keys
  are presed.  In the base class, the arrow keys are used to move the 
  center of the field of view around in the plane, while PAGE_UP & 
  PAGE_DOWN are used to change the Z value of the center of view.

  If you would like to perform other options with the special keys, then 
  when overriding this function, you may want to consider calling 
  GlutWindow2d::processSpecialKeys(key, x, y) in the overridden function
  to maintain the same base functionality.

  @param key The key that was pressed
  @param x The x location of the cursor when the key was pressed
  @param y The y location of the cursor when the key was pressed     
*/
void GlutWindow2d::processSpecialKeys(int key,int x, int y)
{
   switch(key)
   {
   case GL_UP_KEY:
      
      break;

   case GL_DOWN_KEY:
     
      break;

   case GL_LEFT_KEY:
      
      break;

   case GL_RIGHT_KEY:
      
      break;

   case GL_PUP_KEY:
      
      break;

   case GL_PDN_KEY:
      
      break;

   default:
      break;
   }
   
   //Redraw the scene occurs in the base process special keys
   GlutWindow::processSpecialKeys(key, x, y);
}

//********************************************************
/*!
  This is used in conjunction with mouseMotion to provide the camera 
  movement.  Moving the mouse with the left button down rotates the camera 
  around a fixed point (specified by viewX, viewY, viewZ).  Moving the 
  mouse with the right button down zooms the camera in and out
  
  If you would like to perform other operations with the mouse, override
  this function, you may want to consider calling Glut_Window::mouse() 
  in that routine to maintain the mouse interface.
  @param button The button that was pressed
  @param x The x location of the cursor when the button was pressed
  @param y The y location of the cursor when the button was pressed     
*/
void GlutWindow2d::mouse(int button, int state, int x, int y)
{
   if(state == GLUT_UP) 
   {
      buttonDown = -1;
      //enable the flag to not draw the zoom circle
      drawZoomCircleFlag = false;
   }
   else
   {
      //store the button
      buttonDown = button;
      switch(buttonDown)
      {
      case 0:
         //adjust the mouse positions to be from the center of the screen
         mouseStartX = x - windowWidth/2;
         mouseStartY = (windowHeight - y) - windowHeight/2;
         mouseStartZoom = zoomFactor;
         
         //store the current radius for drawing purposes
         zoomDrawRadius = sqrt(x*x + y*y);
         //enable the flag to draw the zoom circle
         drawZoomCircleFlag = true;
         break;
         
      case 2:
         mousePanXOffset = panXOffset;
         mousePanYOffset = panYOffset;

         mouseStartX = x;
         mouseStartY = y;
         
         break;
      }
   }  

   GlutWindow::mouse(button, state, x, y);
}

//********************************************************
/*!
  This routine is called whenever a mouse movement event has occured.
  If you are overriding it, consider calling Glut_Window::mouse_motion()
  in your descendant classes, to maintain the mouse interface
  @param x The x location of the mouse
  @param y The y location of the mouse
*/
void GlutWindow2d::mouseMotion(int x, int y)
{
   switch (buttonDown) 
   {
   case -1:

      return;

   case 2:
      panXOffset = mousePanXOffset + (mouseStartX - x)*zoomFactor;
      //the y pan offset update equation is negative since y goes from 0 to windowHeight from top to bottom
      panYOffset = mousePanYOffset - (mouseStartY - y)*zoomFactor;
   
      setProjectiveSettings();
      
      break;

   case 0:
      //adjust the mouse positions to be from the center of the screen
      x -= windowWidth/2;
      y = (windowHeight - y) - windowHeight/2;

      double origDist = sqrt(mouseStartX*mouseStartX + mouseStartY*mouseStartY);
      double currDist = sqrt(x*x + y*y);

      //store the current radius for drawing purposes
      zoomDrawRadius = currDist;
      
      //adjust the zoom factor based upon the ratio of the radii
      zoomFactor = mouseStartZoom*(origDist/currDist);
      
      
      setProjectiveSettings();
      
      break;


   
   }

   //redraw occurs in the base
   GlutWindow::mouseMotion(x, y);
}

//********************************************************
/*!
  Overide this function to change the location from which the scene is 
  rendered.  It currently uses information from the mouse and the special 
  keyboard keys to set its direction
*/
void GlutWindow2d::positionCamera()
{

}

//********************************************************

void GlutWindow2d::init()
{
   char *dummy_argv[1];
   int dummy_argc = 1;
   dummy_argv[0] = new char[64];
   strcpy(dummy_argv[0], "dummy");
   
   //Initialize glut
   glutInit(&dummy_argc, dummy_argv);
   
   //set the display mode to double-buffered, R
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
   
   //set the window size
   glutInitWindowSize(windowWidth, windowHeight);

   //set the window position
   glutInitWindowPosition(10,10);
   
   // Create the window and save the window ID 
   windowID = glutCreateWindow(myTitle);
   
   //Disabling the depth test (z will not be used in 2D)
   glDisable(GL_DEPTH_TEST);    
   
   //Sets the clear color for when OpenGL clears the entire screen
   glClearColor(0, 0, 0, 1.0f);   

   delete [] dummy_argv[0];
}

//********************************************************

/*! Draws the zoom circle when changing the zoom factor */
void GlutWindow2d::drawZoomCircle()
{
   glColor3f(0.5,0.5,0.5);
   glBegin(GL_LINE_LOOP);
   for(unsigned int theta = 0; theta < 360; theta += 1)
      glVertex2f(cos(theta*M_PI/180)*zoomDrawRadius*zoomFactor + panXOffset, sin(theta*M_PI/180)*zoomDrawRadius*zoomFactor + panYOffset);
   
   glEnd();
}

//********************************************************

/*! Set the projection parameters */
void GlutWindow2d::setProjectiveSettings()
{
   //update the zoom
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity(); 
   glOrtho(-windowWidth/2*zoomFactor + panXOffset, windowWidth/2*zoomFactor + panXOffset, -windowHeight/2*zoomFactor + panYOffset, windowHeight/2*zoomFactor + panYOffset, -1, 1);
   //put it back in GL_MODELVIEW
   glMatrixMode(GL_MODELVIEW);
}

//********************************************************
/*!
  This will draw the given string to the screen at the location given by
  x,y- (relative to the current window properties).
  the color defaults to a gray
*/
void GlutWindow2d::drawText2d(const char *txt, float x, float y, float r, float g, float b, void *font)
{
   if(font == NULL)
      font = GLUT_BITMAP_HELVETICA_10;

   GLboolean lightingOn; 
   GLenum error;
   
   lightingOn = glIsEnabled(GL_LIGHTING);        /* lighting on? */
   
   if(lightingOn) 
      glDisable(GL_LIGHTING);
   
   glPushAttrib(GL_TRANSFORM_BIT); // Save the current matrix mode
   
   glPushAttrib(GL_COLOR_BUFFER_BIT);       /* save current colour */
   
   glColor3f(r, g, b);
   
   glRasterPos3f(x, y, 0);
   
   for(unsigned int i = 0; i < strlen(txt); i++)
      glutBitmapCharacter(font, txt[i]);

   glPopAttrib();
   
   glPopAttrib(); // Restore the original matrix mode

   if(lightingOn)
      glEnable(GL_LIGHTING);
   
   error = glGetError();
   
   if (error != GL_NO_ERROR)
      std::cout << gluErrorString(error) << std::endl;
}
