// Included files for OpenGL Rendering
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
#include <GL/gl.h>
#include <GL/glut.h>
#endif
#include <iostream>
#include <string>
#include <algorithm>
#include <math.h>
#include "image.h"

// ==================================================================
// GLOBAL VARIABLES
// (hard to avoid when using glut & glut callbacks)

int GLOBAL_viewer_width;
int GLOBAL_viewer_height;
std::string GLOBAL_filename;
int GLOBAL_image_width;
int GLOBAL_image_height;
unsigned char *GLOBAL_gl_pixel_data = NULL;


// ==================================================================
// ==================================================================
// READS IN .ppm, .pbm, or .offset files

bool ReadFile() {
  int len = GLOBAL_filename.length();
  if (len > 4 && GLOBAL_filename.substr(len-4) == std::string(".ppm")) {
    static Image<Color> image;
    if( !image.Load(GLOBAL_filename) ) { 
      std::cerr << "Failed to read ppm image" << std::endl;
      return false;
    }
    GLOBAL_image_width = image.Width();
    GLOBAL_image_height = image.Height();
    GLOBAL_gl_pixel_data = image.getGLPixelData();
  } else if (len > 4 && GLOBAL_filename.substr(len-4) == std::string(".pbm")) {
    static Image<bool> image;
    if( !image.Load(GLOBAL_filename) ) { 
      std::cerr << "Failed to read pbm image" << std::endl;
      return false;
    }
    GLOBAL_image_width = image.Width();
    GLOBAL_image_height = image.Height();
    GLOBAL_gl_pixel_data = image.getGLPixelData();
  } else if (len > 7 && GLOBAL_filename.substr(len-7) == std::string(".offset")) {
    static Image<Offset> image;
    if( !image.Load(GLOBAL_filename) ) { 
      std::cerr << "Failed to read offset image" << std::endl;
      return false;
    }
    GLOBAL_image_width = image.Width();
    GLOBAL_image_height = image.Height();
    GLOBAL_gl_pixel_data = image.getGLPixelData();
  } else {
    std::cerr << "Unknown file type: " << GLOBAL_filename << std::endl;
    return false;
  }
  return true;
}

// ==================================================================
// ==================================================================
// GLUT CALLBACKS

// viewport coordinates for 2D
void reshape(int w, int h) {
  GLOBAL_viewer_width = w;
  GLOBAL_viewer_height = h;
  glViewport(0, 0, GLOBAL_viewer_width, GLOBAL_viewer_height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity(); 
  glOrtho(0, GLOBAL_viewer_width, GLOBAL_viewer_height, 0, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

// redraw the pixels
void display(void){
  glClear(GL_COLOR_BUFFER_BIT);
  glPixelZoom(GLOBAL_viewer_width/float(GLOBAL_image_width),
              GLOBAL_viewer_height/float(GLOBAL_image_height));
  glDrawPixels(GLOBAL_image_width, GLOBAL_image_height, 
               GL_RGB, GL_UNSIGNED_BYTE, GLOBAL_gl_pixel_data);
  glFlush();  /* Single buffered, so needs a flush. */
}

// user input 
void keypress(unsigned char key, int x, int y) {
  switch (key) {
  case 'q':  case 'Q':
    exit(0);
    break;
  case 'r':  case 'R':
    std::cout << "re-read image file:  " << GLOBAL_filename;
    if (!ReadFile()) exit(1);
    std::cout << "  (" << GLOBAL_image_width << "x" << GLOBAL_image_height << ")" << std::endl;
    display();
    break;
  default:
    std::cerr << "Error: UNKNOWN KEYBOARD INPUT: " << key << std::endl;
  }
}

// ==================================================================
// ==================================================================

int main(int argc, char *argv[]){
   if(argc != 2)
   { // Take path to image as runtime arg
     std::cerr << "Usage: ./a.out <image.ppm>" << std::endl;
     std::cerr << "   or  ./a.out <image.pbm>" << std::endl;
     std::cerr << "   or  ./a.out <image.offset>" << std::endl;
     exit(1);
   }   

   // Read the image
   GLOBAL_filename = argv[1];
   if (!ReadFile()) { exit(1); }
   int initial_zoom = std::max(2,int(ceil(100/(float)std::min(GLOBAL_image_width,GLOBAL_image_height))));
   GLOBAL_viewer_width = initial_zoom*GLOBAL_image_width;
   GLOBAL_viewer_height = initial_zoom*GLOBAL_image_height;
   
   // Initialize glut
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize(GLOBAL_viewer_width,GLOBAL_viewer_height);
   glutInitWindowPosition(100,100);
   glutCreateWindow(argv[1]);
   glutDisplayFunc(display);
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keypress);
   glutIdleFunc(NULL);

   //for GL speed-ups
   glDisable(GL_DEPTH_TEST);
   glDisable(GL_ALPHA_TEST);

   // For fixing the 4 byte boundary issue 
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   glutMainLoop(); // Give control to glut
}

