//  Starter code for image mosaicing assignment, 15-869
//  by Steve Seitz

//  This code displays an image in a scrollable FLTK window
//  The image is displayed using OpenGL commands
//  Clicking the mouse tells you where in the image the mouse is

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Scrollbar.H>

#include <FL/gl.h>
#include <FL/glut.H>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pic.h>

// Define subclass of GL window
class mosaic_window : public Fl_Gl_Window {
  void draw();
  int handle(int event);
  Pic *img;
public:
  mosaic_window(int w,int h,const char *l=0);
  void setImage(Pic *image);
  float xoff, yoff, scale;
};

// Global variables
Fl_Window *win;
mosaic_window *pane;
Fl_Scrollbar *hscroll, *vscroll;

// Constructor
mosaic_window::mosaic_window(int w,int h,const char *l) :
Fl_Gl_Window(0,0,w,h,l) {
  img = NULL;
  xoff = yoff = 0.0;
  scale = 1.0;
}

// Draw Open GL pane--all drawing code should go here
void mosaic_window::draw() 
{
// the valid() property may be used to avoid reinitializing your
// GL transformation for each redraw:
  if (!valid()) {
	valid(1);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	// Specify window size
	// Don't know why I need to do it this way... the commented out
	// version should work, not this!
//	glViewport(0,0,w(),h());
	glViewport(-int(xoff),0,w()-int(xoff),h());
	// Specify coordinate mapping
//	gluOrtho2D(xoff, w()+xoff, h()+yoff, yoff);
	gluOrtho2D(0, w(), h()+yoff, yoff);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glPixelZoom(1.0,1.0);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	// Update scrollers
	int slidestep = 5;
	int slidex = img->nx-w();
	int slidey = img->ny-h();
	if (slidex < 0) slidex = 0;
	if (slidey < 0) slidey = 0;
	hscroll->range(0, slidex);
	hscroll->slider_size(slidestep/(float)slidex);
	vscroll->range(0, slidey);
	vscroll->slider_size(slidestep/(float)slidey);
  }
  /* draw image, one scanline at a time */
  glClear(GL_COLOR_BUFFER_BIT);
  if (img) {
	for (int y = 0; y < img->ny; y++) {
	  glRasterPos2i(0, y);
	  glDrawPixels(img->nx, 1, GL_RGB, GL_UNSIGNED_BYTE,
				   &(PIC_PIXEL(img, 0, y, 0)) );
	}
  }
  Fl_Gl_Window::draw();
  glFlush();
}

// Handle events 
int mosaic_window::handle(int event) 
{
  float ev_x, ev_y;
  switch(event) {
  case FL_PUSH:
    ev_x = Fl::event_x() + xoff;
    ev_y = Fl::event_y() + yoff;
    fprintf(stderr, "Mouse click at: (%.2f, %.2f)\n", ev_x, ev_y);
    break;
  case FL_KEYBOARD:
    fprintf(stderr, "Key press: %c\n", Fl::event_key());
    switch (Fl::event_key()) {
    case '.':
      scale *= 2.0; 
      invalidate();
      redraw();
      break;
    case ',':
      scale /= 2.0; 
      invalidate();
      redraw();
      break;
    }
    break;
  default:
    ;
  }
  Fl_Gl_Window::handle(event);
  return 1;
}

void mosaic_window::setImage(Pic *image)
{
  img = image;
  redraw();
}

// Scroller callbacks
void hscrollcb(Fl_Scrollbar* scroll)
{
  pane->xoff = (float)scroll->value();
  pane->invalidate();
  pane->redraw();
}
void vscrollcb(Fl_Scrollbar* scroll)
{
  pane->yoff = (float)scroll->value();
  pane->invalidate();
  pane->redraw();
}

int main (int argc, char **argv)
{
  win = new Fl_Window(660, 500, "Mosaic");
  hscroll = new Fl_Scrollbar(0, 480, 640, 20);
  hscroll->type(1);
  hscroll->callback((Fl_Callback*)hscrollcb);
  hscroll->step(5);
  vscroll = new Fl_Scrollbar(640, 0, 20, 480);
  vscroll->type(0);
  vscroll->callback((Fl_Callback*)vscrollcb);
  hscroll->step(5);
  pane = new mosaic_window(640, 480, "pane");
  // Read in input image
  if (argc > 1) {
	Pic *img = tiff_read(argv[1], NULL);
	if(!img) {
	  printf("Unable to open input file '%s'\n", argv[1]);
	} else {
	  printf("Read a %dx%d TIFF image (%d bytes per pixel)\n",
			 img->nx, img->ny, img->bpp); 
	  pane->setImage(img);
	}
  } else {
    fprintf(stderr, "Usage:  mosaic img.tif\n");
    exit(1);
  }
  win->resizable(pane);
  win->end();
  win->show();
  return Fl::run();
}


