/*
 * set your OS!
 *
 * in order to include the correct files...
 */

#define WINDOWS_OS
//#define MAC_OS

/*
 * include files for Windows
 */
#ifdef WINDOWS_OS

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <tchar.h>
#include <math.h>
#include <time.h>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include "BlobResult.h"

#define _NO_MORE_WINSOCK_
#include "SmallSocket.h"
#undef _NO_MORE_WINSOCK_

#define ROOT_FOLDER "C:\\Documents and Settings\\Zachary Dodds\\Desktop\\SetWithOpenCV\\"
#define M_PI 3.1415926535

#endif /* WINDOWS_OS */


#include "mainHelper.h"

/* 
 * global data for passing among 
 *   the main loop (in main)
 *   the mouse-event handler (in mouseEventHandler)
 *   the server (in runServerThread)
 */
IplImage *inputImage=0;  // shared between mouse events and main loop
int swapVerticalValues = 0;  // shared between mouse events and main loop

int drawingRectangle=0;  // shared between mouse events and main loop
CvPoint origin;          // more rectangle-selection data
CvRect selection;

int runAlgorithm = 0;    // shared between server and main loop
int timeToQuitNow = 0;   // shared between server and main loop

ColorDefinition currentColor;  // shared among mouse events, server, and main loop

#include "serverStuff.h"


/*
 * Mouse-event handling routine
 */
void mouseEventHandler( int event, int x, int y, int flags, void* param )
{
  int r,g,b;
	
  if ( !inputImage )  return;  // no image, do nothing
  if (swapVerticalValues)  y = inputImage->height - y;   // platform-dependent!
  
  if ( event == CV_EVENT_MOUSEMOVE && drawingRectangle ) // are we pulling the corner of a rectangle?
  {
    // handle the geometry of the rectangle:
    selection.x = MIN(x,origin.x);  selection.y = MIN(y,origin.y);
    selection.width = MIN( selection.x + CV_IABS(x - origin.x), inputImage->width ) - MAX( selection.x, 0 );
    selection.height = MIN( selection.y + CV_IABS(y - origin.y), inputImage->height ) - MAX( selection.y, 0 );
  } // end of rectangle-drawing
    
  if ( event == CV_EVENT_RBUTTONDOWN ) // did we right-click (or control-click?)
  {
    getrgb( inputImage, x, y, r, g, b );
    printf("      right-click at (%d,%d): rgb=(%d,%d,%d)\n", x, y, r, g, b ) ;
    int N = 10;  // acceptable color width 
    currentColor.expandToFit( r+N, g+N, b+N ); // box of side 2N: upper corner
    currentColor.expandToFit( r-N, g-N, b-N ); // and lower corner
    currentColor.print();
  }
      
  if ( event == CV_EVENT_LBUTTONDOWN ) // did we left-click?
  {
    if (!drawingRectangle) // if we weren't drawing, now we start drawing a rectangle
    {
      origin = cvPoint(x,y);
      selection = cvRect(x,y,1,1);
      drawingRectangle = 1;
    }
    else // if we were drawing a rectangle, now we're finished drawing it
    {
      currentColor.reset();
      for ( int col=selection.x ; col<=selection.x+selection.width ; ++col)
      {
        for ( int row=selection.y ; row<=selection.y+selection.height ; ++row) 
        {
          getrgb( inputImage, col, row, r, g, b );
          currentColor.expandToFit( r, g, b );
        }
      }
      currentColor.print();
      drawingRectangle = 0;
    }
  }
} // end of mouse event handler


/*
 * MAIN
 */
int main( int argc, char** argv )
{	
  /*
   * set up vision server
   */
  int runServer = false;    

#ifdef MAC_OS               
  pthread_t serverThread;   // the identifier for the thread
  if (runServer)            // do we want to run the server?
  {
    printf("Creating serverThread.\n");
    int rc = pthread_create(&serverThread, NULL, runServerThread, NULL);
    if (rc) printf("ERROR in pthread_create: %d.\n", rc);
  }
#endif
#ifdef WINDOWS_OS
  DWORD dwThreadId, dwThrdParam = 1; // nice windows names...
  HANDLE hThread; 
  if (runServer)
  {
	printf("Creating serverThread.\n");
    hThread = CreateThread(          // the thread for our windows server
        NULL,                        // default security attributes 
        0,                           // use default stack size  
        ThreadFunc,                  // thread function 
        &dwThrdParam,                // argument to thread function 
        0,                           // use default creation flags 
        &dwThreadId);                // returns the thread identifier 
	if (!hThread) printf("ERROR in CreateThread.\n"); // if hThread is NULL
  }
#endif
  
  /*
   * set up image input source
   */
  enum { IMAGES_FROM_CAMERA, IMAGES_FROM_FOLDER, IMAGES_FROM_AVI_FILE };
  int captureType = IMAGES_FROM_FOLDER; 
  
  char* MOVIE_NAME = ROOT_FOLDER "path0.mpg"; 
  char* FOLDER_NAME = ROOT_FOLDER "SetImages"; 
  int MAX_FILE_NUMBER = 81;  // the number of images in the folder
  int nextFileNumber = 0;    // the next file number to read 
  
  enum { LIVE, ONEFRAME, STILL };  // how are we now capturing
  int captureStatus = ONEFRAME;
  
  CvCapture* capture = 0;  // the capture object to grab frames from a camera/avi file

  if ( captureType == IMAGES_FROM_CAMERA )  {// initialize "capture," the image source
    capture = cvCaptureFromCAM( 0 );         // camera number zero, which is usually the one attached
  	if (!capture) { 
		printf("Could not open\n%s.\n", MOVIE_NAME);
		printf("Hit enter to quit.\n");
		getchar();
		exit(0);
	}
  }
  else if ( captureType == IMAGES_FROM_AVI_FILE ) {
    capture = cvCaptureFromAVI( MOVIE_NAME ); 
	if (!capture) { 
		printf("Could not open\n%s.\n", MOVIE_NAME);
		printf("Hit enter to quit.\n");
		getchar();
		exit(0);
	}
  }
  else // captureType = IMAGES_FROM_FOLDER;
    capture = 0;
  
  /*
   * create windows for displaying images
   */
  cvNamedWindow( "InputImage", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "InputImage", 100, 100 );
	cvSetMouseCallback( "InputImage", mouseEventHandler, 0 );
  cvNamedWindow( "OutputImage", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "OutputImage", 440, 100 );
  
  /*
   * create an initial image, displayImage, and any other images...
   */
  inputImage = cvCreateImage( cvSize(10,10), 8, 1 );
  IplImage* outputImage = NULL;
  IplImage* frame = NULL;  // image grabbed from camera
  
  /*
   * blob handling
   */
  CBlobResult blobs; 
  CBlob blobToShow;
  
  /*
   * MAIN LOOP
   */
  while (!timeToQuitNow)
  {
    /*
     * INPUT IMAGE SETUP
     *
     * the goal here is to populate the _image_ variable with pixels
     *   depending on the source in captureType
     */
    if (captureStatus != STILL) 
    {
      if (captureType == IMAGES_FROM_FOLDER)
      {
        char filename[255];
        
        if (nextFileNumber > MAX_FILE_NUMBER || nextFileNumber < 0) nextFileNumber = 0;
        sprintf(filename,"%s/image%05d.bmp", FOLDER_NAME, nextFileNumber++);
        inputImage = cvLoadImage(filename, -1); // -1 means determine number of channels from format
      }
      else // captureType == IMAGES_FROM_AVI_FILE or IMAGES_FROM_CAMERA
      {
        frame = cvQueryFrame( capture );  
        if ( !frame  )
        {
          printf( "Frame not obtained when capturing!\n");
          printf( "Setting captureStatus to STILL.\n");
          captureStatus = STILL;
        }
        else  // set up the destination image, if needed
        {
          CvSize frameSize = cvGetSize(frame);
          if ( frameSize.width != inputImage->width || frameSize.height != inputImage->height )
          {
            cvReleaseImage( &inputImage );
            inputImage = cvCreateImage( frameSize, 8, 3 );
            inputImage->origin = frame->origin;
			printf("resizing inputImage.\n");
          } // image is OK and the right size, now
          cvCopy( frame, inputImage, 0 );  // copy the frame data into the image
        }
      }
      
      if (captureStatus == ONEFRAME)  captureStatus = STILL; // we got our one frame...
      // now, we keep that image until an event asks to grab a new one
    } 
    
    /*
     * OUTPUT IMAGE SETUP
     *
     * we need to make sure to set up the output images before processing!
     * (or other intermediate images)  we check to be sure they're the right size...
     */
    if ( outputImage == NULL || 
         ( inputImage->width != outputImage->width  || inputImage->height != outputImage->height ) ) 
    { 
      if (outputImage) cvReleaseImage( &outputImage );
      outputImage = cvCreateImage( cvGetSize(inputImage), 8, 3 );
    }
    cvCopy(inputImage, outputImage, NULL);  // copy input to output image as a starting point for outputImage
    

    /*
     * OUR ALGORITHM/PROCESSING/VISUALIZATION
     */
    if (runAlgorithm)
    {
      int b,r,g;
      // loop over the input image
      for ( int col=0 ; col<inputImage->width ; ++col) 
      {
        for ( int row=0 ; row<inputImage->height ; ++row) 
        {
          
          getrgb( inputImage, col, row, r, g, b );
          if ( currentColor.pixelMatch( r, g, b ) ) 
            setrgb( outputImage, col, row, 0, 255, 0 );
          
        } // end row
      } // end col
    }
        
    /*
		 * DRAW THE IMAGES TO SCREEN
		 */
		if (drawingRectangle)  // shows a yellow rectangle when selecting a region
		  cvRectangle(outputImage, cvPoint(selection.x,selection.y),   // from here
                  cvPoint(selection.x+selection.width, selection.y+selection.height),  // to here
                  CV_RGB(255,255,0),1,8,0);  // yellow, 1 pixel thick, 8-connected
    
    cvShowImage( "InputImage", inputImage );    // render the input image
    cvShowImage( "OutputImage", outputImage );  // render the output image
    
    /*
     * HANDLE KEY PRESSES to an OpenCV window
     */
    char cchar = (char) cvWaitKey(10);   // waits for 10 ms to get a keypress
                                         // use cvWaitKey(0) in order to wait forever...
    if ( cchar == 27 )  break;           // breaks on ESCape key
    
    switch( cchar )                      // handle other key presses
    {        

      case ' ': // stop and get one more frame
      {
        captureStatus = ONEFRAME;
        runAlgorithm = true;
        break;
      }
           
      case 'L': // restart live capturing
      {
        captureStatus = LIVE;
        printf("Getting one frame.\n");
        break;
      }
        
      case 'R': // reset the current color definition
      {
        currentColor.reset();
      }
        
      case 'O': // save params to File
      {
        currentColor.saveToFile( ROOT_FOLDER "colordef.txt" );
        break;
      }
        
      case 'I':  // get params from file
      {
	    currentColor.loadFromFile( ROOT_FOLDER "colordef.txt" );
        break;
      }
        
      case 'A': // turn on or off algorithm running
      {
        if (runAlgorithm) runAlgorithm = false;
        else runAlgorithm = true;
        break;
      }
        
      default:
        break;
    } // end of the keypress-handling switch statement
    
  } // end of the main image input - process - output loop
  
  /*
   * If we're here, some event triggered the main loop to quit
   */
  timeToQuitNow = 1;    // set our signal for the serverThread to quit
  
  printf("Main thread quitting...\n");
#ifdef MAC_OS
  sleep(1.0);             // give it a moment...
#endif
#ifdef WINDOWS_OS
  Sleep(1000);
#endif
  
  /*
   * release the OpenCV resources
   */
  cvReleaseCapture(&capture);
  cvDestroyWindow( "InputImage" );
  cvDestroyWindow( "OutputImage" );
  
  return 0;
}




