// Main 

/*

This code is slightly modified in order to support programming
the game of set. When the user hits the 'S' (capital S), nine
windows holding set cards (from .\\setImages) appear.

Images for the cards and separate images for drawing on top of
the cards are available after this: they are in setimages and
setimages_painted.

The function processSetCard is a place-holder for your code
that will determine the number, color, fill, and shape of the
symbols on a particular set card.

Also, once the code determines the symbols on all nine cards, 
it should somehow indicate which three cards (if any) comprise a set... .
This will have to be done in a function outside processSetCard.


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
How to get OpenCV and this working?

1. Download and install OpenCV for Windows
2. If you don't have visual studio, the "Express Edition" is free - google for it.
3. Double-click the "solution" file (cs154v1.sln)
4. Compile (hopefully!) and run.
5. If you don't have a camera, it's not as much fun :-) Still images are supported, though.
6. Some key presses (with focus on the undistored image window) matter...
   see the code for what each one does. A couple of important ones:
      The 'r' key toggles on and off the selected color
	  The 's' key starts/stops the image grabbing
	  The ' ' (space bar) moves through saved images (in ./VideoBMPs)

7. It is listening on port PORT_NUMBER (default 2000) for a client (in any language)
8. The client.py file can be used as a starting point!

Q. The program quits when you hit the escape key when the focus in on the OpenCV program or if you send it
   a 'q' from the client. 
 
 */



/*
 * a small function to see if j is in the first
 * N of imageIndices's elements
 */
bool alreadyHaveIt(int j, int* imageIndices, int N)
{
	for (int i=0 ; i<N ; ++i)
	  if (imageIndices[i] == j) return true;
	return false;
}


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

#define WINDOWS_OS
//#define MAC_OS

/*
 * include files for Mac OS X
 */
#ifdef MAC_OS

#include <OpenCV/OpenCV.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>       // for memset
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <BlobResult.h>

#include <fstream> 
#include <iostream>
using namespace std;

//#define ROOT_FOLDER "/Users/bfsnet/Desktop/SetWithOpenCV/"
//#define ROOT_FOLDER "/Users/dodds/Desktop/SetWithOpenCV/"
//#define ROOT_FOLDER "/Users/admin/Desktop/SetWithOpenCV/"

#endif

/*
 * 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 NUM_WINDOWS 9


char *setwnd[] = {"set0","set1","set2","set3","set4","set5","set6","set7","set8","set9","set10","set11"};
static IplImage** setimages;
static IplImage** setimage_frames;
static IplImage** setimages_painted;
// a set-card-processing function
void processSetCard(int i);
bool firstTimeForSet = true;



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

#endif /* WINDOWS_OS */


/* 
 * 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 = 1;  // shared between mouse events and main loop

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

int runAlgorithm = 0;    // shared between server and main loop
int timeToQuitNow = 0;   // shared between server and main loop
int blobsAreOn = 1; // allows us to turn blob detection on and off
int removePixels = 0;
int BLOB_FILTER_SIZE_MIN = 50; // changeable with < and >


#define MAXCOLORS 5     // the number of colors to process...

#include "mainHelperBG.h"
BlobData colorData[30];
#include "colorArrayBG.h"
#include "serverStuff.h"    // WINDOWS_OS
// #include "serverStuffSabreen6408.h" MAC_OS
ArrayColorDef currentColorArray;
ArrayColorDef totalColorArray[MAXCOLORS];
char* colorNames[MAXCOLORS];


void pixelsInArraybw(IplImage* INPUT, ArrayColorDef colorArray, IplImage* OUTPUT)
 {
	//printf(" beginning bw \n");
	int imageWidth  = INPUT-> width;
	int imageHeight = INPUT-> height;
	int r,g,b;
	for ( int col = 0; col < imageWidth; col++)
	{
		for ( int row = 0; row < imageHeight; row++)
		{
			getrgb(INPUT, col, row, r, g, b);
			if (colorArray.pixelMatch(r,g,b))
			{
				setbw(OUTPUT, col, row, 255);
				//printf("pixel set");
			}
			else
			{
				setbw(OUTPUT, col, row, 0);
			}
		}
	}
	
		 
}

void drawBorder(IplImage* IMAGE)
{

	int imageWidth  = IMAGE-> width;
	int imageHeight = IMAGE-> height;
	int BORDER_WIDTH = 3;
	
	for( int col = 0; col < BORDER_WIDTH; col++)
	{
		for( int row = 0; row < imageHeight; row++)
		{
			setbw(IMAGE, col, row, 0);
		}
	}
	
	for( int col = imageWidth-BORDER_WIDTH; col < imageWidth; col++)
	{
		for( int row = 0; row < imageHeight; row++)
		{
			setbw(IMAGE, col, row, 0);
		}
	}
	
	for( int col = 0; col < imageWidth; col++)
	{
		for( int row = 0; row < BORDER_WIDTH; row++)
		{
			setbw(IMAGE, col, row, 0);
		}
	}
	
	for( int col = 0; col < imageWidth; col++)
	{
		for( int row = imageHeight-BORDER_WIDTH; row < imageHeight; row++)
		{
			setbw(IMAGE, col, row, 0);
		}
	}
}

/** 
  * totalColorArray handler -- allows us to load in an array with color definitions
  * for each color we look at.
  **/
  
  void tCAHandler()
  {
	char filename[255];
	colorNames[0] = "red";
	colorNames[1] = "yellow";
	colorNames[2] = "blue"; 
	colorNames[3] = "green";
	colorNames[4] = "pink";

	for( int i = 0; i < MAXCOLORS; i++)
	{
		currentColorArray.reset();
		sprintf(filename, "%s%sColorDef%d.txt", ROOT_FOLDER, colorNames[i], COLOR_STEP_SIZE);
		currentColorArray.loadFromFile( filename);
		totalColorArray[i].setAs(currentColorArray);
	}
   }



/*
 * Mouse-event handling routine
 */
void mouseEventHandler( int event, int x, int y, int flags, void* param )
{
  int r,g,b;
  double h,s,v;
	
  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 && !(removePixels)) // did we right-click (or control-click?)
  {
    getrgb( inputImage, x, y, r, g, b );
	RGBtoHSV( r, g, b, h, s, v );
    printf("      right-click at (%d,%d): rgb=(%d,%d,%d)\n", x, y, r, g, b ) ;
	printf("		hsv=(%6.3f,%6.3f,%6.3f)\n",h,s,v);
    int N = 7;  // acceptable color width 
    currentColorArray.expandToFit( r, g, 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_RBUTTONDOWN && removePixels)
  {
	getrgb( inputImage, x, y, r, g, b );
	RGBtoHSV( r, g, b, h, s, v );
    printf("      right-click at (%d,%d): rgb=(%d,%d,%d)\n", x, y, r, g, b ) ;
	printf("		hsv=(%6.3f,%6.3f,%6.3f)\n",h,s,v);
    int N = 7;  // acceptable color width 
    currentColorArray.subtractRange( r, g, 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;
	  printf("begin drawing rectangle. \n");
    }
    else if (drawingRectangle && removePixels) // we'll remove the pixels in the enclosed rectangle
    {
      //currentColorArray.reset();
      for ( int col=selection.x ; col<=selection.x+selection.width ; ++col)
      {
        for ( int row=selection.y ; row<=selection.y+selection.height ; ++row) 
        {
		  int N = 10; // acceptable color width
          getrgb( inputImage, col, row, r, g, b );
          currentColorArray.subtractRange( r, g, b, N);
        }
      }
	  drawingRectangle = 0;
	  printf("end drawing subtraction rectangle. \n");
	  }
	else // drawing rectangle and adding pixels
	{
	  for ( int col=selection.x ; col<=selection.x+selection.width ; ++col)
      {
		for ( int row=selection.y ; row<=selection.y+selection.height ; ++row) 
        {
			int N = 10; // acceptable color width
			getrgb( inputImage, col, row, r, g, b );
			currentColorArray.expandToFit( r, g, b, N);
		}
      }
	  
      drawingRectangle = 0;
	  printf("end drawing addition rectangle. \n");
	  }
  }
	  
	  
	//printf("end of mouse event handler \n");
} // end of mouse event handler


/*
 * MAIN
 */
int main( int argc, char** argv )
{
  /*
   * welcome message :-)
   */

  printf("\n\nWelcome to the vision server!\n\n");

  /*
   * set up vision server
   */
  int runServer = true;    

#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
  //printf("end setting up vision server\n");
  /*
   * set up image input source
   */
  enum { IMAGES_FROM_CAMERA, IMAGES_FROM_FOLDER, IMAGES_FROM_AVI_FILE };
  //int captureType = IMAGES_FROM_CAMERA; 
  int captureType = IMAGES_FROM_AVI_FILE; 
  captureType = IMAGES_FROM_FOLDER;
  if (captureType == IMAGES_FROM_FOLDER) swapVerticalValues = 0;
  
  char* MOVIE_NAME = ROOT_FOLDER "nomad.avi"; 
  char* FOLDER_NAME = ROOT_FOLDER "setImages"; 
  //printf("FOLDER_NAME: %s", FOLDER_NAME);

  /* get the number of images available...
   */
  char filename[255];
  sprintf( filename, "%s\\numberOfImages.txt",  FOLDER_NAME );
  FILE* imageNumberFile = fopen( filename, "r" );
  int MAX_FILE_NUMBER = 0;  // the number of images in the folder
  fscanf(imageNumberFile, "%d", &MAX_FILE_NUMBER);
  fclose( imageNumberFile );
  printf("There are %d images available.\n", MAX_FILE_NUMBER ); // off by one..
  int nextFileNumber = 0;    // the next file number to read - start at 0
  // need to handle if there are really no images!
  int imageNumber = MAX_FILE_NUMBER+1;		// for saving _subsequent_ images!
  
  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
  else if ( captureType == IMAGES_FROM_AVI_FILE )
    capture = cvCaptureFromAVI( MOVIE_NAME ); 
  else // captureType = IMAGES_FROM_FOLDER;
  {
    //capture = cvCaptureFromCAM( 0 ); // try it, in case there's a camera attached...
	capture = 0;
  }
  //printf("end image input setup\n");
  
  /*
   * create windows for displaying images
   */
  cvNamedWindow( "InputImage", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "InputImage", 100, 100 );
  //cvSetMouseCallback( "InputImage", mouseEventHandler, 0 );  // Crazy, but it just might work
  cvNamedWindow( "OutputImage", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "OutputImage", 440, 100 );
  cvSetMouseCallback( "OutputImage", mouseEventHandler, 0);
  cvNamedWindow( "BinaryImage", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "BinaryImage", 220, 400 );
  
	//printf("created windows for displaying images\n");
  /*
   * 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
  IplImage* binaryImage = NULL;
  //printf("created inital images\n");
  
  /*
   * blob handling
   */
  CBlobResult blobs; 
  CBlob blobToShow;	

  /*
   * Load in the tCA
   */
	if (blobsAreOn)		tCAHandler();

  printf("Back in main... .\n");
  
  /*
   * MAIN LOOP
   */
  while (!timeToQuitNow)
  {//printf("beginning main loop\n");
    /*
     * 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)
      {       
        if (nextFileNumber > MAX_FILE_NUMBER || nextFileNumber < 0)  // is the file number in bounds?!?
		{
			nextFileNumber = 0; // wrap around!
		}
        
#ifdef MAC_OS
		sprintf(filename,"%s\\image%04d.bmp", FOLDER_NAME, nextFileNumber);
#endif
#ifdef WINDOWS_OS
		sprintf(filename,"%s\\image%05d.bmp", FOLDER_NAME, nextFileNumber);
#endif
		// update for the next time...
		nextFileNumber = nextFileNumber + 1;
		// now read in the image
		//printf("Reading image named %s .\n", filename);
        inputImage = cvLoadImage(filename, -1); // -1 means determine number of channels from format
		printf("Read image named %s .\n", filename);
		//printf("the inputImage is %d .\n", (int)inputImage); 
		if (inputImage == 0)
		{
			printf("There was a problem with reading image %s !\n", filename);
		}
      }
      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);
          /* REDUCTION_FACTOR
           */
          
          /* old code
          if ( frameSize.width != inputImage->width || frameSize.height != inputImage->height )
          {
            cvReleaseImage( &inputImage );
            inputImage = cvCreateImage( frameSize, 8, 3 );
            inputImage->origin = frame->origin;
          } // image is OK and the right size, now
          cvCopy( frame, inputImage, 0 );  // copy the frame data into the image
           end of OLD CODE
           */
          int REDUCTION_FACTOR = 1;
          int desiredInputWidth = (frameSize.width/REDUCTION_FACTOR);
          int desiredInputHeight = (frameSize.height/REDUCTION_FACTOR);
          if ( desiredInputWidth != inputImage->width || desiredInputHeight != inputImage->height )
          {
            cvReleaseImage( &inputImage );
            CvSize newInputImageSize = cvSize(desiredInputWidth, desiredInputHeight);
            inputImage = cvCreateImage( newInputImageSize, 8, 3 );
            inputImage->origin = frame->origin; // hmmm?
          } // image is OK and the right size, now
          /* copy it over ... */
          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) 
            {
              // get pixel from across the full frame
              getrgb( frame, col*REDUCTION_FACTOR, row*REDUCTION_FACTOR, r, g, b );
              // place into the inputImage
              setrgb( inputImage, col, row, r, g, b);
            } // end row
          } // end col
        } // end grab of camera image ("else")
      } // end of captureType == IMAGES_FROM_AVI_FILE or IMAGES_FROM_CAMERA block 
      
      if (captureStatus == ONEFRAME)  captureStatus = STILL; // we got our one frame...
      // now, we keep that image until an event asks to grab a new one
	//printf("finished input image setup\n InputImage : %s", inputImage);
      
      
    } // end of (captureStatus != STILL) block
		/* WINDOWS COMMENTED CODE
        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;
          } // 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
	//printf("finished input image setup\n InputImage : %s", inputImage);
    }
	*/
    
    /*
     * 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 );
	  outputImage->origin = inputImage->origin; // try this...
    }

    cvCopy(inputImage, outputImage, NULL);  // copy input to output image as a starting point for outputImage
	//cvCopy(outputImage2, outputImage, NULL); // just ttry it!
    
    if ( binaryImage == NULL ||
          ( inputImage->width != binaryImage->width || inputImage->height != binaryImage->height ) )
    {
      if (binaryImage) cvReleaseImage( &binaryImage );
      binaryImage = cvCreateImage( cvGetSize(inputImage), 8, 1);
	  binaryImage->origin = inputImage->origin;
    }
	// default binary image if runAlgoithm and blobsAreOn are both off...
	// pixelsInArraybw( inputImage, currentColorArray, binaryImage);
	
				
    /*
	 * end of image setup
	 */
	


    /*
     * OUR ALGORITHM and PROCESSING
     */
    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 ( currentColorArray.pixelMatch(r,g,b) )
          {
			setrgb( outputImage, col, row, 0, 255, 255 ); // a color distinct from the others...
		  }
        } // end row
      } //printf("end algorithm processing\n");// end col
    }
	
	if(blobsAreOn)
	{
		//CBlob allDestBlobs [30];
		CBlob destinationBlobs [3];


		for(int i=0; i < MAXCOLORS; ++i)
		{ 
			pixelsInArraybw(inputImage, totalColorArray[i], binaryImage);
			//cvErode(binaryImage, binaryImage, NULL, ERODE_AMOUNT);
			//cvDilate(binaryImage, binaryImage, NULL, 2);
			drawBorder(binaryImage); // be sure border pixels are ignored (BORDER_WIDTH)
			//cvErode(binaryImage, binaryImage, NULL, 2);

			// run connected components
			blobs = CBlobResult( binaryImage, NULL, 100, true );

			// get only blobs that are large enough...
			// get blobs with more than 50 pixels
            blobs.Filter( blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, 50); 
            
            // get blobs whose gray-scale value is greater than 200
            // keep in mind that we are "blobbing" the binary image, so any value
            // between 1 and 254 would work!
            blobs.Filter( blobs, B_INCLUDE, CBlobGetMean(), B_GREATER, 200 ); 


			//blobs.Filter( blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, BLOB_FILTER_SIZE_MIN);
			// Here, we could filter out the long rectangular blobs. We'd only go to it if we had single
			// color blobs, as there won't be many long ones if we have 2 or 3 color blobs.
			// This might end up in the main python file as well.
			// tCA = {red, yellow, blue, green, pink}
			int rr = (i==0 )? 255: (i==1)? 255: (i==2)? 0:   (i == 3)? 0:   255; //gets the proper color for the different cases
			int gg = (i==0)? 0:    (i==1)? 255: (i==2)? 0:   (i == 3)? 255:  20;
			int bb = (i==0)? 0:    (i==1)? 0:   (i==2)? 255: (i == 3)? 0:   147;
			
			CBlobGetArea getArea = CBlobGetArea(); 
			CBlobGetMinX getMinX = CBlobGetMinX();   
			CBlobGetMaxX getMaxX = CBlobGetMaxX();
			CBlobGetMinY getMinY = CBlobGetMinY();
			CBlobGetMaxY getMaxY = CBlobGetMaxY();
			//printf("-------------------------\n");
			for(int j=0; j<3;j++)
			{
				blobs.GetNthBlob( CBlobGetArea(), j, destinationBlobs[j] ); 
				//double area = getArea(destinationBlobs[j]);
				//printf("area = %f\n", area);
				int minxarea = (int) getMinX(destinationBlobs[j]);
				int maxxarea = (int) getMaxX(destinationBlobs[j]);
				int minyarea = (int) getMinY(destinationBlobs[j]);
				int maxyarea = (int) getMaxY(destinationBlobs[j]);
				colorData[i].xmin[j]=minxarea;
				colorData[i].xmax[j]=maxxarea;
				colorData[i].ymin[j]=minyarea;
				colorData[i].ymax[j]=maxyarea;
				colorData[i].colour= i;
				//printf("\nafter assignment\n");
				//printf("minx = %d\n", colorData[i].xmin[j]);
				//printf("maxx = %d\n", colorData[i].xmax[j]);
				//printf("miny = %d\n", colorData[i].ymin[j]);
				//printf("maxy = %d\n", colorData[i].ymax[j]);
				//printf("colour number is: %d\n\n", colorData[i].colour);  //1 is red, 2 is yellow, 3 is blue, 4 is green 
				destinationBlobs[j].FillBlob( outputImage, CV_RGB( rr, gg , bb ) );
			}
			//allDestBlobs[i] = destinationBlobs;
			//CBlobGetArea getArea = CBlobGetArea();
		} // end of for(int i=0; i < MAXCOLORS; ++i)
	} // end of if(blobsAreOn)

	/*
	 * DRAWING IMAGES
	 */
	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
    cvShowImage( "BinaryImage", binaryImage );  // render the binary image
    


    /*
     * HANDLE KEY PRESSES to an OpenCV window
     */
	char filename[255];
    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;
        //printf("Going to single-frame mode.\n");
        break;
      }

	  case '<': // make BLOB_FILTER_SIZE_MIN smaller by half
      {
		 BLOB_FILTER_SIZE_MIN /= 2;
		 if (BLOB_FILTER_SIZE_MIN < 1)  BLOB_FILTER_SIZE_MIN = 1;
		 printf("The minimum detectable blob is of now size %d.\n", BLOB_FILTER_SIZE_MIN);
		 break;
	  }

	  case '>': // make BLOB_FILTER_SIZE_MIN larger by 100
      {
		 BLOB_FILTER_SIZE_MIN += 100;
		 if (BLOB_FILTER_SIZE_MIN > 1000000)  BLOB_FILTER_SIZE_MIN = 1000000;
		 printf("The minimum detectable blob is of now size %d.\n", BLOB_FILTER_SIZE_MIN);
		 break;
	  }
           
      case 'L': // restart live capturing
      {
        captureStatus = LIVE;
        printf("Going to live mode.\n");
        break;
      }

	  /* 'C' and 'F' are unlikely to work!!
	   * because of the need to initialize things correctly...
	   * try it out...
	   */
	  case 'C': // go to camera
	  {
		if (capture != 0)
		{
			captureType = IMAGES_FROM_CAMERA;
			printf("Switching to Camera");
		}
		else
		{
			printf("No camera detected upon start up!\n");
		}
		break;
	  }

	  case 'F': // go to folder
	  {
		captureType = IMAGES_FROM_FOLDER;
		printf("Switching to Folder");
		break;
	  }
        
      case 'R': // reset the current color definition
      {
        currentColorArray.reset();
		break;
      }
        
      case 'A': // turn on or off algorithm running
      {
        if (runAlgorithm) runAlgorithm = false;
        else runAlgorithm = true;
		printf("Algorithm Status is %d .\n", runAlgorithm);
        break;
      }
      
      case 'B': //run backwards through the pictures
      {
        nextFileNumber = nextFileNumber - 2;
        if (nextFileNumber < 0) nextFileNumber = MAX_FILE_NUMBER;
        captureStatus = ONEFRAME;
        //runAlgorithm = true;
        break;
      }

	  case 'S': // save the current image
	  {
		sprintf(filename, "%stmp/image%05d.bmp", ROOT_FOLDER, imageNumber);
		printf("Saving File: %s \n", filename);
		cvSaveImage(filename, inputImage);

		// update file numbers...
		MAX_FILE_NUMBER = imageNumber;
		imageNumber = MAX_FILE_NUMBER + 1;  // get ready for the next one...

		// write this out to file...
		sprintf( filename, "%s\\numberOfImages.txt",  FOLDER_NAME );
		imageNumberFile = fopen( filename, "w" );
		fprintf(imageNumberFile, "%d\n", MAX_FILE_NUMBER);
		fclose( imageNumberFile );
		printf("There are %d images available.\n", MAX_FILE_NUMBER ); // off by one...

		break;
	  }

	  case '0': // turns blobs on or off
	  {
		if (blobsAreOn) blobsAreOn = false;
		else blobsAreOn = true;
		printf("blobsAreOn Status is %d .\n", blobsAreOn);
		break;
	  }

	  case 'P': // print current array
	  {
		currentColorArray.print();
		break;
	  }
	  
	  case '!': // save alternate color array (Red)
	  {
		sprintf(filename, "%sredColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.saveToFile(filename);
        break;
	  }

	  case '@': // save alternate color defintions (Yellow)
	  {
		sprintf(filename, "%syellowColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.saveToFile(filename);
        break;
	  }

	  case '#': // save alternate color defintions (Blue)
	  {
		char filename[255];
		sprintf(filename, "%sblueColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.saveToFile(filename);
        break;
	  }

	  case '$': // save alternate color defintions (Green)
	  {
		sprintf(filename, "%sgreenColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.saveToFile(filename);
        break;

	  }
	  
	  case '%': // save alternate color defintions (Pink)
	  {
		sprintf(filename, "%spinkColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.saveToFile(filename);
        break;

	  }
	  
	  case '1': // load alternate color array (Red)
	  {
		currentColorArray.reset();
		sprintf(filename, "%sredColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.loadFromFile(filename);
        break;
	  }

	  case '2': // load alternate color array (Yellow)
	  {
		currentColorArray.reset();
		sprintf(filename, "%syellowColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.loadFromFile(filename);
        break;
	  }

	  case '3': // load alternate color array (Blue)
	  {
		currentColorArray.reset();
		sprintf(filename, "%sblueColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.loadFromFile(filename);
        break;	  
	  }

	  case '4': // load alternate color array (Green)
	  {
		currentColorArray.reset();
		sprintf(filename, "%sgreenColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.loadFromFile(filename);
        break;
	  }
	  
	  case '5': // load alternate color array (Pink)
	  {
		currentColorArray.reset();
		sprintf(filename, "%spinkColorDef%d.txt", ROOT_FOLDER, COLOR_STEP_SIZE);
		currentColorArray.loadFromFile(filename);
        break;
	  }
	  
	  case 'Z': // Reload color definitons
	  {
		tCAHandler();
		break;
	  }
	   
	  case '-': // remove pixels from current definiton
	  {
		if (removePixels)
			removePixels = false;
		else removePixels = true;
		break;
	  }

	    /*
		 * a lower-case 's' toggles playing set...
		 */
	  case 's':
		{
			captureStatus = ONEFRAME;       // we're no longer capturing...
			char filenameset[500];          // holds our set file names... 

			if (firstTimeForSet)     // need to set up the windows if it's
			{                        // the first time playing set...
				for (int i=0 ; i<NUM_WINDOWS ; ++i) {  
					cvNamedWindow( setwnd[i], CV_WINDOW_AUTOSIZE );
					cvMoveWindow( setwnd[i], i%3 * 335, i/3 * 280);  
				} 
				srand( (unsigned)time( NULL ) );       // seed for future random #s
				cout << "Setting up set!" << endl;     // Hi!
				setimages = new IplImage*[NUM_WINDOWS]; // should delete these somewhere...
				setimages_painted = new IplImage*[NUM_WINDOWS]; // for overlays

				for (int i=0 ; i<NUM_WINDOWS ; ++i)
				{
					sprintf(filenameset,"..\\setImages\\image%05d.bmp",i);
					//cvReleaseImage(&undis_frame);
					setimages[i] = cvLoadImage(filenameset); // load image0 numWindows times.
					setimages_painted[i] = cvLoadImage(filenameset);
					cout << "Setting up window " << i << " named " << setwnd[i] << endl;
					cvShowImage(setwnd[i], setimages[i]);
				}	

				firstTimeForSet = false; // make sure we do this only once
			}
			// don't really want an "else" here
			{
				cout << "Reading in random images..." << endl;

				int imageIndices[NUM_WINDOWS];         // list of indices
				for (int i=0 ; i<NUM_WINDOWS ; ++i) {  // set them all to -1
					imageIndices[i] = -1; 
				}

				for (int i=0 ; i<NUM_WINDOWS ; ++i)
				{
					cout << "i is " << i << endl;     // keep us updated
					while (true) 
					{
						int randImage = (int)(81.0*rand()/RAND_MAX);
						if (randImage < 0 || randImage > 80) continue;
						cout << "  randImage is " << randImage << endl;
						
						if (!alreadyHaveIt(randImage,imageIndices,i))
						   imageIndices[i] = randImage;
						else
						{
							cout << "Have it ";
							for (int i=0 ; i<NUM_WINDOWS ; ++i)
								cout << imageIndices[i] << " ";
							cout << endl;
							continue;
						}
						break;
					}

					/*
					 * now load image i
					 */
					sprintf(filenameset,".\\setImages\\image%05d.bmp",imageIndices[i]);
					cout << "Reading in the file " << filenameset << " for #" << i << endl;
					//cvReleaseImage(&(setimages[i]));
					setimages[i] = cvLoadImage(filenameset);         // for proceesing
					setimages_painted[i] = cvLoadImage(filenameset); // for showing results
					cout << "Setting up window " << i << " named " << setwnd[i] << " with image #" << imageIndices[i] << endl;
					cvShowImage(setwnd[i], setimages[i]);
				}
			}
			/*
			 * now, process the cards
			 */
			int i=3;
			processSetCard( i );

		} // end of case 's'
        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" );
  cvDestroyWindow( "BinaryImage" );
  
  return 0;
}


/*
 * here is where the set-processing happens
 * for image #i
 */
void processSetCard(int i) 
{
	// we know that the images to process are in setimages,
	// and we will draw on setimages_painted
    IplImage* sourceImage = setimages[i];
	IplImage* destinationImage = setimages_painted[i];
	char* destinationWindow = setwnd[i];

	// show where the point 30,30 is in the image
	CvPoint pt; pt.x = 30; pt.y = 200;
	cvCircle( destinationImage, pt, 10,  CV_RGB(255,255,0), /*thickness*/ 4 );

	// show some pixel manipulation - pixels are handled as uchar*'s
	int r,g,b,realrow;
	uchar* sourcepix;     // a pixel in the input
	uchar* destpix;   // a pixel in the output
	int row_offset = -50;
	int col_offset = -50;
	for (int row=10 ; row<30 ; ++row)
	{
		for (int col=200 ; col < 230 ; ++col)
		{
			// realrow = row;
			realrow = sourceImage->height - row;
			sourcepix = cvPtr2D(sourceImage,realrow,col);    // get the pixel we care about
			r = (int)(sourcepix[2]);  // the red component of the pixel we care about
			g = (int)(sourcepix[1]);  // the green component of the pixel we care about
			b = (int)(sourcepix[0]);  // the blue component of the pixel we care about
			destpix = cvPtr2D(destinationImage,realrow+row_offset,col+col_offset);    // get the pixel we (might) change
			destpix[0] = 0; /*blue*/ destpix[1] = 0; /*green*/ destpix[2] = 255; /*red*/
		}
	}
	cvShowImage(destinationWindow, destinationImage);
}


