/*
 *   IMPROV
 *
 *   by  Thomas Braunl   <braunl@ee.uwa.edu.au>
 *   and Michael Rudolph <rudolpml@hermes.informatik.uni-stuttgart.de>
 *   based on XFQCAM by Paul Chinn <loomer@svpal.org>
 *
 *   QuickCam code by Thomas Davis
 *   Additional QuickCam code Scott Laird <scott@laird.com>
 *   Adapted by Thomas Braunl, Uni Stuttgart, 1996
 *   Color adapted by Thomas Braunl, UWA, 1998
 *
 *   Patch for 16bpp displays by Brian Beaudoin <baddog@darkknight.net>
 *   1997/11/29 (sorry, had to do it, 24bpp is broke on my machine)
 *
 *   $Id: xfqcam.c,v 2.00 1998/02/16 12:08:30 braunl Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <unistd.h>
#include <math.h>
#include <malloc.h>
#include <forms.h>
#include "controlpanel.h"
#include "xfqcam.h"
#include "improc.h"

/*
 *    Static variables visible only to the main module
 */

static void (*RawToX)();
static void (*GetImage)(BYTE *);
static Display * disp;
static Window  win;
static XGCValues values;
static Colormap cmap;
static int xDepth;
static XImage *ximage;
static BYTE ximagemem[CIMAGE_SIZE*4];
static GC gc;
static int imageSize  =  IMAGE_SIZE;
/* static int CimageSize = CIMAGE_SIZE; */
static int screen_num;
static int quitFlag = 0;
static int flagAutoBrightness = 0;
static int flagHistogram;
static char fpstext[80] = "";
static char changetext[80]= "";
static int fakeargc = 1;
static char *fakeargv[2];
static int privatecmap=0;
static struct timeval tv1, tv2;
static double framerate=0, fr;

static double * iplParam;
static FL_OBJECT * lastBrowser;
static int lastObj = -1;
static int lastIndex = -1;
static E_IPLOP editChoice;
static int editMode;

static FD_AboutBox * aboutBox;
static FD_HGram * hg;

static MenuNameArg (*pNameArg)[];

/*
 *    some global variable for the IPL parameters and operations
 */

FD_QuickCam * mainwin;
IPL_CONFIG _IPLConfig;
#define CSIZE 256
int colortable[CSIZE];
volatile int stopFlag;
BYTE lastImageBuffer[CIMAGE_SIZE];
BYTE extraImageBuffer[CIMAGE_SIZE];

typedef struct _stag
{
  int w, h;
  char label[10];
} SCREENMENU;

static MenuItemDetails menParamInfo[] = {
  {"General(g/c)", M_General, -1},
  {"Nop", E_NOP, 0},
  {"Identity", E_Identity, 0},
  {"Neg", E_Negation, 0},
  {"Diff", E_Difference, 0},
  {"Noise(t)", E_Noise, 1},
  {"Show(x,y)", E_ShowXY, 2},
  {"Overlay(g)/(rgb)", E_Overlay, 3},

  {"Format", M_Format, -1},
  {"Col to grey", E_ColGray, 0},
  {"Grey to bin(t)", E_Threshold, 1},
  {"Grey to dither", E_Dithering, 0},
  {"Grey stretch", E_GrayStretch, 0},
  {"Grey reduce", E_GrayReduce, 0},

  {"Morphology", M_Morphology, -1},
  {"Erosion", E_Erosion, 0},
  {"Dilation", E_Dilation, 0},
  {"Open", E_Open, 0},
  {"Close", E_Close, 0},
  {"Fill(x,y)", E_Fill, 2},
  {"Connected(x,y)", E_Connected, 2},
  {"Boundary", E_Boundary, 0},
  {"Skeleton", E_Skeleton, 0},

  {"Edges", M_Edges, -1},
  {"Minimum\t(3x3)", E_Min, 0},
  {"Maximum\t(3x3)", E_Max, 0},
  {"Mean\t\t(3x3)", E_Mean, 0},
  {"Median\t\t(3x3)", E_Median, 0},
  {"Laplace\t\t(3x3)", E_Laplace, 0},
  {"Sobel\t\t(3x3)", E_Sobel, 0},
  {"Corner\t\t(3x3)", E_Corner, 0},

  {"Colour Ops(c)", M_Colour, -1},
  {"Match(r,g,b)", E_MatchCol, 3},
  {"TestPalette", E_TestPalette, 0},

  {"Advanced(g)", M_Advanced, -1},
  {"Region (t)", E_Region, 1},
  {"Find circles", E_Circles, 0}
};
  
static const char * const helpText[] = {
  "IMPROV - Help File",
  " ",
  " ",
  "1. IMPROV - Image Processing for Robot Vision",
  " ",
  "IMPROV is a simple tool to test image processing functions.",
  "Images are taken from the QUICKCAM. Operations may be concatenated, so",
  "even a more complex task can also be tested. The captured camera image is",
  "shown in the image window #1.",
  " ",
  "2. Selecting and adding image processing operations",
  " ",
  "The dropchoice on top of each image allows for the selection of",
  "an image processing operation to be performed on the previous image",
  "and displayed in the current image window.",
  "The chosen operation is displayed in a browser control below the",
  "image window. Multiple operations can be successively applied by",
  "concatenating them pushing the '+' button to the left of the browser.",
  "As long as the operation is not appended to the existing list of",
  "operations it may be replaced by some other selection from the dropchoice.",
  "Some operations take arguments like the threshold function. Up to three",
  "paramters can be set using the slider controls labeld p1, p2 and p3.",
  "Paramter values lie in the range [0.0..1.0]. A 0.5 value for the threshold",
  "functions means an actual greyscale threshold of 8 (4bpp = 16 different",
  "grey values).",
  " ",
  " ",
  "3. Modifying and deleting image processing operations",
  " ",
  "Changes of the parameter slider controls are effective for the most",
  "recent selected image processing operation. Parameters can be changed",
  "afterwards by first selecting them in the corresponding browser and then",
  "adjusting the parameter slider. A selected operation gets highlighted the",
  "'+' push button changes into a '-' push button. Pressing it will remove the",
  "operation from the list.",
  " ",
  "4. The image processing operations",
  " ",
  "Here is a brief summary of the image processing operations available",
  "in IMPROV.",
  " ",
  "NOP            No Operation",
  "Identity       Copy source image to target image",
  "Negation       Negate source image (p`=maxcol-p)",
  "Dither(2x2)    Target image contains only black/white pixels",
  "Difference     Difference image of source and previous source",
  "Threshold      Binarize image. Threshold is given by P1",
  " ",
  "Col. to Gray   Convert color image to grayscale",
  "Match Color    Select RGB color with slide rulers to match image",
  " ",
  "Minimum        3x3 Minimum Filter",
  "Maximum        3x3 Maximum Filter",
  "Mean           3x3 Mean Filter",
  "Median         3x3 Median Filter",
  "Laplace        3x3 Laplace Filter",
  "Sobel          3x3 Sobel Filter (absolute gradient value)",
  "Corner         3x3 Corner Filter",
  " ",
  "Erosion        3x3 Erosion",
  "Dilation       3x3 Dilation",
  "Open           3x3 Erosion and then Dilation",
  "Close          3x3 Dilation and the Erosion",
  "Fill",
  "Connected",
  "Boundary",
  "Skeleton",
  " ",
  "Noise          Generate Noise in target image (P1=degree of noise)",
  "Grey stretch   Make use of all grey values",
  "Grey reduce",
  "Show (x,y)     Print grey/color value of pixel at (x=P1,y=P2)",
  "Overlay        Copy all pixels of the original where source image pixels",
  "               are black",
  "Region (t)     Segementation (t=P1 as threshold)",
  "Find Circles   Find small circles in source images",
  " ",
  " ",
  " ",
  "5. Links",
  " ",
  "IMPROV - Image Processing for Robot Vision",
  "http://www.ee.uwa.edu.au/~braunl/improv",
  "ftp://ftp.ee.uwa.edu.au/users/braunl/improv",
  " ",
  NULL
};

static const char * const aboutText[] = {
  " ",
  "@c@m@bIMPROV - Image Processing for Robot Vision",
  "@c@iSee it live at the IMPROV !",
  "@c@i@bversion 2.01 23 Mar 98",
  " ",
  "@cby Thomas Braunl and Michael Rudolph <braunl@ee.uwa.edu.au>",
  "@ccolor by Thomas Braunl and Elliot Nicholls <nicho-ej@ee.uwa.edu.au>",
  "@cparts of the IPL by Gerrit Heitsch <heitscgt@hermes...>",
  "@cThe Univ. of Western Australia, E&E Eng., CIIPS 1998",
  "@coriginally based on XFQCam and QuickCam code",
  "@cby Paul Chinn <loomer@1000klub.com>",
  "@cThomas Davis and Scott Laird <scott@laird.com>",
  "@cwith additions and modifications by Paul Chinn",
  "@cpatch for 16bpp displays by Brian Beaudoin <baddog@darkknight.net>",
  " ",
  "@chttp://www.ee.uwa.edu.au/~braunl/improv",
  "@cftp://ftp.ee.uwa.edu.au/users/braunl/improv",
  " ",
  "@cThis program is free for noncommercial use.",
  NULL};

static void CalcXferMode(void);
static int NewImage(int, int);
static void RawTo8BitX(BYTE * raw, BYTE * xbuf, int col);
static void RawTo16BitX(char *raw, short *xbuf, int col);
static void RawTo24BitX(BYTE * raw, int * xbuf, int col);
static void xqc_createpalette(Colormap cmap, int colcam, int depth);
static void ExitXWindows(void);
static char * GenerateBrowserText(int imageNumber, int n);
static void DeselectBrowser(FL_OBJECT * browser);

/*
 *    FORMS callback functions
 */

void About(FL_OBJECT *obj, long val)
{
  int i;

  fl_clear_browser(aboutBox -> AboutText);

  for(i = 0; aboutText[i] != NULL; i++)
    fl_add_browser_line(aboutBox -> AboutText, aboutText[i]);

  fl_show_form(aboutBox -> AboutBox,
	       FL_PLACE_MOUSE,
	       FL_TRANSIENT,
	       "About IMPROV");
}

void AboutOk(FL_OBJECT *obj, long val)
{
  fl_hide_form(aboutBox -> AboutBox);
}

void Help(FL_OBJECT * obj, long val)
{
  int i;

  fl_clear_browser(aboutBox -> AboutText);

  for(i = 0; helpText[i] != NULL; i++)
    fl_add_browser_line(aboutBox -> AboutText, helpText[i]);

  fl_show_form(aboutBox -> AboutBox,
	       FL_PLACE_MOUSE,
	       FL_TRANSIENT,
	       "IMPROV Help");
}

void LoadSave(FL_OBJECT * obj, long val)
{
  const char * msg = (val == 0) ? "Load" : "Save";
  const char * fileName = fl_show_fselector(msg,
					    ".",
					    "*.dat",
					    "noname.dat");

  if (fileName != NULL && val == 1)
    QCAMSaveFile(fileName);
  if (fileName != NULL && val == 0)
    QCAMLoadFile(fileName);
}

void QCAMSaveFile(const char * fileName)
{
  int i, j;

  FILE * outFile = fopen(fileName, "w");
  if (outFile == NULL)
    return;

  fputs("IMPROV Data File\n\n", outFile);

  fprintf(outFile, "%d %d %d\n", _QCConfig.brightness,
	  _QCConfig.whitebalance,
	  _QCConfig.contrast);

  for (i = 0; i < NO_OF_IMAGES; i++)
    {
      fprintf(outFile, "%d\n", iplCount[i]);
      for (j = 0; j < iplCount[i]; j++)
        {
	  fprintf(outFile, "%d ", iplOps[i][j].operation);
	  fprintf(outFile, "%1.4f ", iplOps[i][j].param[0]);
	  fprintf(outFile, "%1.4f ", iplOps[i][j].param[1]);
	  fprintf(outFile, "%1.4f ", iplOps[i][j].param[2]);
	  fprintf(outFile, "\n");
        }
    }

  fclose(outFile);

  chown(fileName, getuid(), getgid());
}

void QCAMLoadFile(const char * fileName)
{
  int i, j;
  char line[256];

  FILE * inFile = fopen(fileName, "r");
  if (inFile == NULL)
    return;

  fgets(line, sizeof(line), inFile);
  fgets(line, sizeof(line), inFile);

  fscanf(inFile, "%d %d %d\n", &_QCConfig.brightness,
	 &_QCConfig.whitebalance,
	 &_QCConfig.contrast);

  for (i = 0; i < NO_OF_IMAGES; i++)
    {
      fscanf(inFile, "%d\n", &iplCount[i]);

      for (j = 0; j < iplCount[i]; j++)
        {
	  int temp;

	  fscanf(inFile, "%d ", &temp);
	  iplOps[i][j].operation = (E_IPLOP)temp;

	  fscanf(inFile, "%lf ", &iplOps[i][j].param[0]);
	  fscanf(inFile, "%lf ", &iplOps[i][j].param[1]);
	  fscanf(inFile, "%lf ", &iplOps[i][j].param[2]);
        }
    }

  fclose(inFile);
}

void Stop(FL_OBJECT * obj, long val)
{
  stopFlag = fl_get_button(obj);
}

void Slider(FL_OBJECT *obj, long val)
{
  switch (val)
    {
    case 0:
      _QCConfig.brightness = fl_get_slider_value(obj);
      QC_set_brightness();
      break;

    case 1:
      _QCConfig.whitebalance = fl_get_slider_value(obj);
      QC_set_whitebalance();
      break;

    case 2:
      _QCConfig.contrast = fl_get_slider_value(obj);
      QC_set_contrast();
      break;
    }
}

static void DeselectBrowser(FL_OBJECT * browser)
{
  editMode = MODE_ADD;
  fl_deselect_browser(browser);

  if (browser == mainwin -> FD_BROWSER_1)
    fl_set_object_label(mainwin -> FD_DELETE_1, "+");
  else if (browser == mainwin -> FD_BROWSER_2)
    fl_set_object_label(mainwin -> FD_DELETE_2, "+");
  else if (browser == mainwin -> FD_BROWSER_3)
    fl_set_object_label(mainwin -> FD_DELETE_3, "+");
  else if (browser == mainwin -> FD_BROWSER_4)
    fl_set_object_label(mainwin -> FD_DELETE_4, "+");
  else if (browser == mainwin -> FD_BROWSER_5)
    fl_set_object_label(mainwin -> FD_DELETE_5, "+");
}

void XQC_Browser(FL_OBJECT * obj, long val)
{
  int selection;
  int imageNumber = (int)val;

  if (lastBrowser != NULL && lastBrowser != obj &&
      fl_get_browser(lastBrowser) != 0)
    DeselectBrowser(lastBrowser);

  lastBrowser = obj;
  selection = fl_get_browser(obj);

  if (selection > 0)
    {
      lastObj = imageNumber;
      lastIndex = selection - 1;
      iplParam = &iplOps[imageNumber][selection-1].param[0];

      fl_set_slider_value(mainwin -> FD_PARAM_1, iplParam[0]);
      fl_set_slider_value(mainwin -> FD_PARAM_2, iplParam[1]);
      fl_set_slider_value(mainwin -> FD_PARAM_3, iplParam[2]);

      editMode = MODE_DEL;

      switch (imageNumber)
        {
	case 0:
	  fl_set_object_label(mainwin -> FD_DELETE_1, "-");
	  break;
	case 1:
	  fl_set_object_label(mainwin -> FD_DELETE_2, "-");
	  break;
	case 2:
	  fl_set_object_label(mainwin -> FD_DELETE_3, "-");
	  break;
	case 3:
	  fl_set_object_label(mainwin -> FD_DELETE_4, "-");
	  break;
	case 4:
	  fl_set_object_label(mainwin -> FD_DELETE_5, "-");
	  break;
        }
    }
}

void XQC_Button(FL_OBJECT * obj, long val)
{
  int selection;
  int imageNumber = (int)val;
  FL_OBJECT * browser = NULL;
  char * browserText;

  switch (imageNumber)
    {
    case 0:
      browser = mainwin -> FD_BROWSER_1;
      break;
    case 1:
      browser = mainwin -> FD_BROWSER_2;
      break;
    case 2:
      browser = mainwin -> FD_BROWSER_3;
      break;
    case 3:
      browser = mainwin -> FD_BROWSER_4;
      break;
    case 4:
      browser = mainwin -> FD_BROWSER_5;
      break;
    default:
      return;
    }

  if (editMode == MODE_DEL)
    {
      selection = fl_get_browser(browser);

      if (selection > 0)
        {
	  fl_delete_browser_line(browser, selection);

	  while (selection < iplCount[imageNumber])
            {
	      iplOps[imageNumber][selection-1] = iplOps[imageNumber]
		[selection];

	      browserText = GenerateBrowserText(imageNumber, selection-1);
	      fl_replace_browser_line(browser, selection, browserText);
	      selection++;
            }

	  iplCount[imageNumber]--;

	  if (iplCount[imageNumber] == 0)
            {
	      iplCount[imageNumber] = 1;
	      iplOps[imageNumber][0].operation = E_NOP;
	      browserText = GenerateBrowserText(imageNumber, 0);
	      fl_add_browser_line(browser, browserText);
            }

	  DeselectBrowser(browser);
        }
    }
  else if (editMode == MODE_ADD)
    {
      int n;

      if (lastBrowser != NULL && fl_get_browser(lastBrowser) != 0)
        {
	  DeselectBrowser(lastBrowser);
	  lastBrowser = NULL;
        }

      /*
       *    Get the current number of successive image operations and
       *    increment the counter. Store the operation code and parameters
       *    in the op array.
       */

      n = iplCount[imageNumber];
      if (n < MAX_IPL_OPS)
        {
	  iplParam = &iplOps[imageNumber][n-1].param[0];
	  iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
	  iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
	  iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);

	  iplCount[imageNumber]++;
	  iplOps[imageNumber][n].operation = E_NOP;
	  iplParam = &iplOps[imageNumber][n].param[0];
	  lastIndex = n;
	  lastObj = imageNumber;

	  browserText = GenerateBrowserText(imageNumber, n-1);
	  fl_replace_browser_line(browser, n, browserText);
	  browserText = GenerateBrowserText(imageNumber, n);
	  fl_add_browser_line(browser, browserText);
	  fl_set_browser_topline(browser, n + 1);
        }
    }
}

void XQC_Param(FL_OBJECT * obj, long val)
{
  if (iplParam != NULL)
    iplParam[val] = fl_get_slider_value(obj);

  if (lastObj >= 0 && lastIndex >= 0)
    {
      char * browserText = GenerateBrowserText(lastObj, lastIndex);
      FL_OBJECT * browser = NULL;

      if (lastObj == 0)
	browser = mainwin -> FD_BROWSER_1;
      else if (lastObj == 1)
	browser = mainwin -> FD_BROWSER_2;
      else if (lastObj == 2)
	browser = mainwin -> FD_BROWSER_3;
      else if (lastObj == 3)
	browser = mainwin -> FD_BROWSER_4;
      else if (lastObj == 4)
	browser = mainwin -> FD_BROWSER_5;

      fl_replace_browser_line(browser, lastIndex + 1, browserText);
    }
}

void XQC_graph1(FL_OBJECT *obj, long val)
{
  int n;
  FL_OBJECT * browser = NULL;

  /*
   *    If we switch to anoter image save the parameter values (slider
   *    values) in the corresponding structure.
   */

  if (iplParam != NULL)
    {
      iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
      iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
      iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);
    }

  /*
   *    Determine the new image and the operation to be performed
   */

  editChoice = fl_get_menu(obj);
  editMode = MODE_ADD;

  if (obj == mainwin -> FD_graph1)
    {
      lastObj = 0;
      browser = mainwin -> FD_BROWSER_1;
      fl_set_object_label(mainwin -> FD_DELETE_1, "+");
    }
  else if (obj == mainwin -> FD_graph2)
    {
      lastObj = 1;
      browser = mainwin -> FD_BROWSER_2;
      fl_set_object_label(mainwin -> FD_DELETE_2, "+");
    }
  else if (obj == mainwin -> FD_graph3)
    {
      lastObj = 2;
      browser = mainwin -> FD_BROWSER_3;
      fl_set_object_label(mainwin -> FD_DELETE_3, "+");
    }
  else if (obj == mainwin -> FD_graph4)
    {
      lastObj = 3;
      browser = mainwin -> FD_BROWSER_4;
      fl_set_object_label(mainwin -> FD_DELETE_4, "+");
    }
  else if (obj == mainwin -> FD_graph5)
    {
      lastObj = 4;
      browser = mainwin -> FD_BROWSER_5;
      fl_set_object_label(mainwin -> FD_DELETE_5, "+");
    }

  n = iplCount[lastObj];
  if (n < MAX_IPL_OPS)
    {
      lastIndex = n - 1;
      iplParam = &iplOps[lastObj][n-1].param[0];
      iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
      iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
      iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);
      iplOps[lastObj][n-1].operation = editChoice;
      fl_replace_browser_line(browser,
			      n,
			      GenerateBrowserText(lastObj, lastIndex));

      if (lastBrowser != NULL && fl_get_browser(lastBrowser) != 0)
        {
	  DeselectBrowser(lastBrowser);
	  lastBrowser = NULL;
        }

    }
}

static char * GenerateBrowserText(int imageNumber, int n)
{
  static char browserText[80];
  char partialText[80];
  E_IPLOP operation = iplOps[imageNumber][n].operation;
  const char *s = (*pNameArg)[operation-1].name;
  int args = (*pNameArg)[operation-1].args;
  int len = strcspn(s, "(");

  strncpy(partialText, s, len);
  partialText[len] = '\0';

  switch (args) {
  case 1:
    sprintf(&browserText[0],
	    "%d.%s(%1.2g)",
	    n + 1,
	    partialText,
	    iplOps[imageNumber][n].param[0]);
    break;
    
  case 2:
    sprintf(&browserText[0],
	    "%d.%s(%1.2g,%1.2g)",
	    n + 1,
	    partialText,
	    iplOps[imageNumber][n].param[0],
	    iplOps[imageNumber][n].param[1]);
    break;
    
  case 3:
    sprintf(&browserText[0],
	    "%d.%s(%1.2g,%1.2g,%1.2g)",
	    n + 1,
	    partialText,
	    iplOps[imageNumber][n].param[0],
	    iplOps[imageNumber][n].param[1],
	    iplOps[imageNumber][n].param[2]);
    break;
    
  default:
    sprintf(&browserText[0], "%d.%s", n + 1, s);
  }

  return &browserText[0];
}

void Zoom(FL_OBJECT *obj, long val)
{
  _QCConfig.zoom = val;
  CalcXferMode();
}

void Histogram(FL_OBJECT *obj, long val)
{
}

void automode(FL_OBJECT *obj, long val)
{
  flagAutoBrightness = fl_get_button(obj);
}

void Graph(FL_OBJECT *obj, long val)
{
  flagHistogram = fl_get_button(obj);

  if (flagHistogram)
    fl_show_form(hg -> HGram, FL_PLACE_FREE, FL_TRANSIENT, "Histogram");
  else
    fl_hide_form(hg -> HGram);
}

void Quit(FL_OBJECT *obj, long val)
{
  quitFlag = 1;
}

int QCAMinit(int *colCam, int *camVer, int *conVer)
{
  int i;
  int menus[64];
  int numMenus = 0;
  char buffer[500];

  if (_QCConfig.quickcam_port != 0)
    {
      if(_QCConfig.quickcam_port != 0x278 &&
	 _QCConfig.quickcam_port != 0x378 &&
	 _QCConfig.quickcam_port != 0x3bc)
        {
	  fprintf(stderr, "Invalid port address\n");
	  return (1);
        }

      ioperm(_QCConfig.quickcam_port,3,1);

    }
  else
    {
      _QCConfig.quickcam_port = QC_find();
      if (_QCConfig.quickcam_port < 0)
        {
	  fprintf(stderr,"Can't get IO perms\n");
	  return(1);
        }
      else if(_QCConfig.quickcam_port == 0)
        {
	  fprintf(stderr,"Can't find a camera\n");
	  return 1;
        }
    }

  /* reset camera */
  QC_reset(_QCConfig.quickcam_port);
  /* get camera version (colour? b/w?) */
  QC_getversion(_QCConfig.quickcam_port, colCam, camVer, conVer);
  QC_reset(_QCConfig.quickcam_port);

  fakeargv[0] = malloc(10);
  fakeargv[1] = malloc(10);
  strcpy(fakeargv[0], "robot");
  strcpy(fakeargv[1], "hello");
  disp = fl_initialize(&fakeargc, fakeargv, 0, 0, 0);

  if(_QCConfig.zoom > 2 || _QCConfig.zoom < 0)
    {
      fprintf(stderr, "Zoom values are: 0(no zoom), 1(1.5x), 2(2x)\n");
      return 1;
    }

  mainwin = create_form_QuickCam();
  aboutBox = create_form_AboutBox();
  hg = create_form_HGram();

  /*intialize various control setting*/
  fl_set_slider_bounds(mainwin->Brightness, 0, 255);
  fl_set_slider_step(mainwin->Brightness, 1);
  fl_set_slider_precision(mainwin->Brightness, 0);
  fl_set_slider_bounds(mainwin->Contrast, 0, 255);
  fl_set_slider_step(mainwin->Contrast, 1);
  fl_set_slider_precision(mainwin->Contrast, 0);
  fl_set_slider_bounds(mainwin->WhiteBal, 0, 255);
  fl_set_slider_step(mainwin->WhiteBal, 1);
  fl_set_slider_precision(mainwin->WhiteBal, 0);

  mainwin->FD_fps->label = fpstext;
  mainwin->FD_rez->label = changetext;

    
  /* New menues for image processing are defined below */
  /* Also set up linked list of MenuNameArg structs for lookups */

  pNameArg = (MenuNameArg (*)[(sizeof(menParamInfo)/sizeof(menParamInfo[0]))])calloc((sizeof(menParamInfo)/sizeof(menParamInfo[0])), sizeof(MenuNameArg));
    
  menus[0] = fl_newpup(fl_default_win());
  fl_setpup_bw(menus[0], -2);
  fl_setpup_shadow(menus[0], 0);

  for(i=0; i < (sizeof(menParamInfo)/sizeof(menParamInfo[0])); i++) {
    if(menParamInfo[i].args == -1) {
      menus[++numMenus] = fl_newpup(0);
      fl_setpup_shadow(menus[numMenus], 0);
      sprintf(buffer, "%s%%m%d", menParamInfo[i].name, menParamInfo[i].ref);
      fl_addtopup(menus[0], buffer, menus[numMenus]);
    } else {
      sprintf(buffer, "%s%%x%d", menParamInfo[i].name, menParamInfo[i].ref);
      (*pNameArg)[menParamInfo[i].ref - 1].name = menParamInfo[i].name;
      (*pNameArg)[menParamInfo[i].ref - 1].args = menParamInfo[i].args;
      fl_addtopup(menus[numMenus], buffer);
    }
  }

  fl_set_menu_popup(mainwin -> FD_graph1, menus[0]);
  fl_set_menu_popup(mainwin -> FD_graph2, menus[0]);
  fl_set_menu_popup(mainwin -> FD_graph3, menus[0]);
  fl_set_menu_popup(mainwin -> FD_graph4, menus[0]);
  fl_set_menu_popup(mainwin -> FD_graph5, menus[0]);


  for (i = 0; i < iplCount[0]; i++)
    fl_add_browser_line(mainwin -> FD_BROWSER_1,
			GenerateBrowserText(0, i));

  for (i = 0; i < iplCount[1]; i++)
    fl_add_browser_line(mainwin -> FD_BROWSER_2,
			GenerateBrowserText(1, i));

  for (i = 0; i < iplCount[2]; i++)
    fl_add_browser_line(mainwin -> FD_BROWSER_3,
			GenerateBrowserText(2, i));

  for (i = 0; i < iplCount[3]; i++)
    fl_add_browser_line(mainwin -> FD_BROWSER_4,
			GenerateBrowserText(3, i));

  for (i = 0; i < iplCount[4]; i++)
    fl_add_browser_line(mainwin -> FD_BROWSER_5,
			GenerateBrowserText(4, i));

  /* New menues for image processing are defined above */

  /*set the sliders to qcam values*/
  fl_set_slider_value(mainwin->Brightness, _QCConfig.brightness);
  fl_set_slider_value(mainwin->Contrast, _QCConfig.contrast);
  fl_set_slider_value(mainwin->WhiteBal, _QCConfig.whitebalance);

  CalcXferMode();

  fl_show_form(mainwin -> QuickCam,
	       FL_PLACE_SIZE,
	       FL_FULLBORDER,
	       "IMPROV - Image Processing for Robot Vision");

  xDepth = fl_state[fl_get_vclass()].depth;

  if(xDepth == 8 || xDepth == 4)
    RawToX = RawTo8BitX;
  else if (xDepth == 16)
    RawToX = RawTo16BitX;
  else if (xDepth == 24)
    RawToX = RawTo24BitX;
  else
    {
      fprintf(stderr, "IMPROV only works in 8/16/24 bit X depths\n");
      fprintf(stderr, "This diplay has a %d bit color depth\n", xDepth);
      QC_exit();
      exit(1);
    }

  screen_num = DefaultScreen(disp);
  gc = XCreateGC(disp, FL_ObjWin(mainwin -> FD_image1), 0, &values);

  if (privatecmap)
    {
      cmap = XCreateColormap(disp,
			     win,
			     DefaultVisual(disp, screen_num),
			     AllocNone);

      XSetWindowColormap(disp, win, cmap);
    }
  else
    cmap = DefaultColormap(disp, screen_num);

  xqc_createpalette(cmap, *colCam, xDepth);

  ximage = NULL;
  NewImage(_QCConfig.xsize, _QCConfig.ysize);

  /* Attempt to get permission to access IO ports.  Must be root */

  setuid(getuid());

  QC_reset(_QCConfig.quickcam_port);
  QC_set_all();

  gettimeofday(&tv1,NULL);

  return 0;
}

int QCAMset(double thr, int con, int bri, int whi)
{
  if (thr != 0.0) _QCConfig.thresh       = thr;
  if (con != 0)   _QCConfig.contrast     = con;
  if (bri != 0)   _QCConfig.brightness   = bri;
  if (whi != 0)   _QCConfig.whitebalance = whi;

  return 0;
}

int QCAMget(BYTE rawbuf[], int * ret_chg, int * ret_quit, int col)
{
  int i;
  long pixelDelta;

  fl_check_forms();

  *ret_chg = 0;
  *ret_quit = quitFlag;

  if (stopFlag)
    return 0;

  GetImage(rawbuf);
  RawToX(rawbuf, &ximagemem[0], col);

  XPutImage(disp,
	    FL_ObjWin(mainwin -> FD_image1),
	    gc,
	    ximage,
	    0,
	    0,
	    mainwin -> FD_image1 -> x + 4,
	    mainwin -> FD_image1 -> y + 4,
	    ximage -> width,
	    ximage -> height);

  XFlush(disp);

  /* Calculate frame rate */
  gettimeofday(&tv2, NULL);

  /* The frame rate is calculated using the TCP RTT algorithm */
  fr = (1.0 / (tv2.tv_sec - tv1.tv_sec +
	       (tv2.tv_usec-tv1.tv_usec) / 1000000.0));

  if (framerate != 0)
    framerate = 0.9 * framerate + 0.1 * fr;
  else
    framerate = fr;

  sprintf(fpstext, "Rate: %.2f fps", framerate);
  fl_redraw_object(mainwin -> FD_fps);

  tv1.tv_sec  = tv2.tv_sec;
  tv1.tv_usec = tv2.tv_usec;

  for (i = 0, pixelDelta = 0; i < imageSize; i++)
    pixelDelta += (lastImageBuffer[i] - rawbuf[i]) *
      (lastImageBuffer[i] - rawbuf[i]);

  memcpy(lastImageBuffer, rawbuf, imageSize);

  sprintf(changetext, "change: %2.2f", sqrt((double)pixelDelta / imageSize));
  fl_redraw_object(mainwin -> FD_rez);

  if (flagHistogram)
    {
      int i;
      int histo[256];
      float fhisto[256];
      float fxz[256];

      IPL_gen_histogram(rawbuf, &histo[0]);

      for (i = _IPLConfig.colorBlack; i <= _IPLConfig.colorWhite; i++)
        {
	  fxz[i] = i;
	  fhisto[i] = histo[i];
        }

      fl_set_xyplot_ybounds(hg -> Histogram, 0, imageSize / 3);
      fl_set_xyplot_data(hg -> Histogram,
			 &fxz[0],
			 &fhisto[0],
			 _IPLConfig.colorWhite - _IPLConfig.colorBlack + 1,
			 "Histogram",
			 "Intensity",
			 "Pixels");
    }

  if (flagAutoBrightness)
    {
      int i;
      long sum = 0;
      double mean;
      static double tempz = 0.0;
      static double tempint = 0.0;

      for (i = 0; i < imageSize; i++)
	sum += rawbuf[i];
      mean = (double)sum / imageSize;

      if (mean > 8)
	tempint = - MAX_AUTO_ADJUST * ((mean - 8) / 7);
      else if (mean < 7)
	tempint = MAX_AUTO_ADJUST * (1 - (mean / 7));

      if (mean > 8 || mean < 7)
        {
	  tempz = fl_get_slider_value(mainwin -> Brightness);
	  tempz += tempint;

	  if (tempz >= 255)
	    tempz = 254;

	  if (tempz <= 0)
	    tempz = 1;

	  fl_set_slider_value(mainwin -> Brightness, tempz);
	  Slider(mainwin -> Brightness, 0);
        }
    }

  return 0;
}

int QCAMput(BYTE raw[], int imageNumber, int col)
{
  int x, y;

  switch (imageNumber)
    {
    case 0:
      x = mainwin -> FD_image1 -> x + 5;
      y = mainwin -> FD_image1 -> y + 5;
      break;
    case 1:
      x = mainwin -> FD_image2 -> x + 5;
      y = mainwin -> FD_image2 -> y + 5;
      break;
    case 2:
      x = mainwin -> FD_image3 -> x + 5;
      y = mainwin -> FD_image3 -> y + 5;
      break;
    case 3:
      x = mainwin -> FD_image4 -> x + 5;
      y = mainwin -> FD_image4 -> y + 5;
      break;
    case 4:
      x = mainwin -> FD_image5 -> x + 5;
      y = mainwin -> FD_image5 -> y + 5;
      break;
    case 5:
      x = mainwin -> FD_image6 -> x + 5;
      y = mainwin -> FD_image6 -> y + 5;
      break;
    default:
      return -1;
    }

  RawToX(raw, &ximagemem[0], col);

  XPutImage(disp,
	    FL_ObjWin(mainwin -> FD_image1),
	    gc,
	    ximage,
	    0,
	    0,
	    x,
	    y,
	    ximage -> width,
	    ximage -> height);

  XFlush(disp);

  return 0;
}


int QCAMexit()
{
  QC_reset(_QCConfig.quickcam_port);
  ExitXWindows();

  return 0;
}

static void ExitXWindows(void)
{
}


static void RawTo8BitX(BYTE * raw, BYTE * xbuf, int col)
{ int i, r,g,b;

if (col)
  for(i = 0; i < imageSize; i++)
    {
      r = (int) ((*raw++ >> 6) & 0x7f) <<  4;
      g = (int) ((*raw++ >> 6) & 0x7f) <<  2;
      b = (int) ((*raw++ >> 6) & 0x7f);
      *xbuf++ = colortable[ 16 + (r | g | b) ];
    }
else for(i = 0; i < imageSize; i++)
  *xbuf++ = colortable[(int)*raw++];
}      

static void RawTo16BitX(char *raw, short *xbuf, int col)
{ int   i, r,g,b;

if (col)
  for(i = 0; i < imageSize; i++)
    {
      r = (int) ((*raw++ >> 3) & 0x7f) << 11;
      g = (int) ((*raw++ >> 2) & 0x7f) <<  5;
      b = (int) ((*raw++ >> 3) & 0x7f);
      *xbuf++ = (short) (r | g | b);
    }
else for(i = 0; i < imageSize; i++)
  *xbuf++ = colortable[(int)*raw++];
}

static void RawTo24BitX(BYTE * raw, int * xbuf, int col)
{ int i,c, r,g,b;

if (col)
  for(i = 0; i < imageSize; i++) {
    r = (int) *raw++;
    g = (int) *raw++;
    b = (int) *raw++;
    *xbuf++ = (int) ((r<<16) | (g<8) | b);
  }
else for(i = 0; i < imageSize; i++) {
  c = (int)*raw++;
  *xbuf++ = c;
}
}


static void xqc_createpalette(Colormap cmap, int colcam, int depth)
{ int i;
XColor col,c;

for(i=0; i<16; i++) {
  col.red = col.green = col.blue = i * 4096;
  if (!XAllocColor(disp, cmap, &col))
    fprintf(stderr,"XAllocColor failed on %d\n",i);
  colortable[i] = col.pixel;
}

if (colcam && (depth==8))  /* additional color part */
  {   i = 16;
  for(c.red  = 0; c.red  < 4; c.red++)
    for(c.green= 0; c.green< 4; c.green++)
      for(c.blue = 0; c.blue < 4; c.blue++)
	{
	  col.red  = (65536 / 3) * c.red;
	  col.green= (65536 / 3) * c.green;
	  col.blue = (65536 / 3) * c.blue;
	  if (!XAllocColor(disp, cmap, &col))
	    fprintf(stderr,"XAllocColor failed on %d\n",i);
	  colortable[i++] = col.pixel;
	}
  }
}


static int NewImage(int w, int h)
{
  if (ximage == NULL || w != ximage -> width || h != ximage -> height)
    {
      if (ximage != NULL)
	XDestroyImage(ximage);

      ximage = XCreateImage(disp,
			    DefaultVisual(disp,screen_num),
			    xDepth,
			    ZPixmap,
			    0,
			    &ximagemem[0],
			    w,
			    h,
			    8,
			    0);
      return 1;
    }

  return 0;
}

static void CalcXferMode()
{
  
  /* Check for invalid frame modes */
  /* Set decimation mode */
  switch (_QCConfig.zoom) {
  case 0: /* 4:1 decimation */
    _QCConfig.xfermode = 8;
    if((_QCConfig.xsize * 4 > 320) || (_QCConfig.ysize * 4 > 240)) {
      fprintf(stderr, "Image %d x %d too big for camera!\n", _QCConfig.xsize, _QCConfig.ysize);
      exit(1);
    }
    break;
  case 1: /* 2:1 decimation */
    _QCConfig.xfermode = 4;
    if((_QCConfig.xsize * 2 > 320) || (_QCConfig.ysize * 2 > 240)) {
      fprintf(stderr, "Image %d x %d too big for camera!\n", _QCConfig.xsize, _QCConfig.ysize);
      exit(1);
    }
    break;
  case 2: /* No decimation */
    _QCConfig.xfermode = 0;
    if((_QCConfig.xsize > 320) || (_QCConfig.ysize > 240)) {
      fprintf(stderr, "Image %d x %d too big for camera!\n", _QCConfig.xsize, _QCConfig.ysize);
      exit(1);
    }
    break;
  default: /* Unknown state */
    fprintf(stderr, "Zoom values are: 0(no zoom), 1(1.5x), 2(2x)\n");
    exit(1);
  }

    
  if(_QCConfig.colour) {
    /* shift bits appropriately for colour config */
    _QCConfig.xfermode = _QCConfig.xfermode >> 1;
    _QCConfig.xfermode ^= 24;
  } else {
    if (_QCConfig.bpp6)
      _QCConfig.xfermode += 2;
  }

  _QCConfig.xfermode += _QCConfig.biMode;

  if(_QCConfig.colour) {
    GetImage = QC_CGet4bppImageUni;
  } else {
    GetImage = QC_Get4bppImageUni;
  }
}



