#include "ProcImage.h"
#include "FW/labImage.h"
#include <stdlib.h>

#define DEBUG_IMPROV 0

// the operations menu
// format:
//     Field 1 - "Submenu/Operation"
//     Field 2 - Enumerated type
//     Field 3 - Number of arguments
//

//Global variables which need to be reset! 
//for E_Optic_Flow
int count_pic;
//for E_difference
extern int buffer;
int count_pic_liu;

static Improv_Menu_Item menu_items[] = {

  {"General/Identity", E_Identity, 0},
  {"General/Camera", E_CamImg, 0},
  {"General/Negation", E_Negation, 0},
  {"General/Difference", E_Difference, 0},
  {"General/Noise(t)", E_Noise, 1},
  {"General/Show(x,y)", E_ShowXY, 2},
  
  {"Format/Col to grey", E_ColGray, 0},
  {"Format/RGB to HSV", E_HSV, 0},
  {"Format/Grey to bin(t)", E_Threshold, 1},
  {"Format/Grey to dither", E_Dithering, 0},
  {"Format/Grey stretch", E_GrayStretch, 0},
  {"Format/Grey reduce(t)", E_GrayReduce, 1},
  
  {"Morphology/Erosion", E_Erosion, 0},
  {"Morphology/Dilation", E_Dilation, 0},
  {"Morphology/Open", E_Open, 0},
  {"Morphology/Close", E_Close, 0},
  {"Morphology/Fill(x,y)", E_Fill, 2},
  {"Morphology/Connected(x,y)", E_Connected, 2},
  {"Morphology/Boundary", E_Boundary, 0},
  {"Morphology/Skeleton", E_Skeleton, 0},
  
  {"Edges/Minimum\t(3x3)", E_Min, 0},
  {"Edges/Maximum\t(3x3)", E_Max, 0},
  {"Edges/Mean\t(3x3)", E_Mean, 0},
  {"Edges/Median\t(3x3)", E_Median, 0},
  {"Edges/Laplace\t(3x3)", E_Laplace, 0},
  {"Edges/Sobel\t(3x3)", E_Sobel, 0},
  {"Edges/Corner\t(3x3)", E_Corner, 0},
  
  {"Advanced/Region(t)", E_Region, 1},
  {"Advanced/Contour(t)", E_Contour,1},
  {"Advanced/Moravec", E_Moravec,0},
  {"Advanced/Track", E_Track, 0},
  {"Advanced/Histogram Equallization", E_HistEq, 0},
  {"Advanced/Gaussian Smoothing", E_Gauss, 0},           
  {"Advanced/Match color range(r,g,b)", E_Match_Color_Range, 3},
   
  {"Optical Flow/Correlation(h,t,r)", E_Just_Optic_Flow_Camus, 3},
  {"Optical Flow/Differential(h,t,r)", E_Just_Optic_Flow_Liu, 3}, 
};


extern IPL_CONFIG ipl_config;
ProcImage::ProcImage(int x, int y, int w, int h, const char *label)
            : Fl_Group(x, y, w, h)
{
  menu = new Fl_Menu_Button(x+65, y, 105, 20, "Operation");
  menu->box(FL_THIN_UP_BOX);
  menu->labelsize(12);
  menu->textsize(12);
  init_menu();
  
  //Display area
  box = new Fl_Box(x, y+20,w,h-45, label);
  box->box(FL_ENGRAVED_BOX);
  box->labelsize(12);
  box->align(FL_ALIGN_TOP_LEFT);
  
  //+
  button = new Fl_Button(x, y+h-22, 25, 25, "+");
  button->callback(button_cb);
  
  //Text box(was 25)
  browser = new Fl_Browser(x+25, y+h-25,w-25, 32); 
  browser->type(2);
  browser->color(49);
  browser->labelsize(12);
  browser->textsize(12);
  browser->callback(browser_cb);
  browser->has_scrollbar(2);

  num_ops = 0;
  
  param[0] = param[1] = param[2] = 0.5;
  param_cb = NULL;
  
  have_focus = false;
  
  initialised = false; 

  enlarge=true;
}


void ProcImage::initialize(Picture *p_in/*Picture *in*/, 
			   Picture *p_out, 
			   Picture *p_cam, 
			   bool *in_colour,
                           bool *out_colour, 
			   bool *cam_colour)
{
  //Set actual image type -> is always defined in the previous class
  p_input_img=p_in;
  
  //Set actual image type
  setImageType(p_out,ipl_config.imageType);
  resizeImage(p_out,ipl_config.imageWidth,ipl_config.imageHeight);
  p_output_img=p_out;
  clearImage(p_output_img);
  
  //Set Camera Image
  setImageType(p_cam,ipl_config.imageType);
  resizeImage(p_cam,ipl_config.imageWidth,ipl_config.imageHeight);
  p_camera_img=p_cam; 
  
  //Set internal images for processing
  p_src=copyImage(p_in);
    
  //Destination
  p_dst=copyImage(p_out);
   
  input_colour = in_colour;
  output_colour = out_colour;
  camera_colour = cam_colour;
  
  //Global flags for processing routines
  count_pic=0;
  buffer=0;
  count_pic_liu=0;

  //Initialising done!
  initialised = true;
}


void ProcImage::set_param_cb(void (*cb)(double p[]))
{
    param_cb = cb;
}

#ifndef MAX
#define MAX(a,b) ((a)>=(b)?(a):(b))
#endif
#ifndef MIN
#define MIN(a,b) ((a)>=(b)?(b):(a))
#endif
void ProcImage::draw()
{
   
  is_colour = *input_colour;
  
  if (initialised)
    process();
  
  Fl_Group::draw();
  
  //Have to have at least one operation...
  if(num_ops!=0)
    {//p_output_img must exist..
      if(p_output_img)
	{//Color or Grey?...
	  if (p_output_img->format==pix_rgb24) 
	    {//Crop image? ...
	      if(( p_output_img->width  > ipl_config.displayWidth ||
		   p_output_img->height > ipl_config.displayHeight ) && 
		 enlarge==true )
		{
		  static Picture *st_tmp;
		  
		  st_tmp=cropImage(p_output_img,0,0, 
				MIN(ipl_config.displayWidth,
				    p_output_img->width), 
				MIN(ipl_config.displayHeight,
				    p_output_img->height));
		  //Cropped version
		  fl_draw_image(st_tmp->data,
				x()+ipl_config.display_xOffset,
				y()+ipl_config.display_yOffset,
				st_tmp->width,
				st_tmp->height, 3, 0);
		  
		  freeImage(st_tmp);
		}
	      else
		fl_draw_image(p_output_img->data,
			      x()+ipl_config.display_xOffset,
			      y()+ipl_config.display_yOffset,
			      p_output_img->width,
			      p_output_img->height,3,0);
	    }
	  else
	    {//Grey Image ...
	      if(( p_output_img->width  > ipl_config.displayWidth ||
		   p_output_img->height > ipl_config.displayHeight ) && 
		 enlarge==true )
		{
		  Picture *tmp;
	      
		  tmp=cropImage(p_output_img,0,0, 
				MIN(ipl_config.displayWidth,
				    p_output_img->width), 
				MIN(ipl_config.displayHeight,
				    p_output_img->height));
		  //Cropped version
		  fl_draw_image_mono(tmp->data,
				     x()+ipl_config.display_xOffset,
				     y()+ipl_config.display_yOffset,
				     tmp->width,
				     tmp->height, 1, 0); 
		  freeImage(tmp);
		}
	      else
		fl_draw_image_mono(p_output_img->data,
				   x()+ipl_config.display_xOffset,
				   y()+ipl_config.display_yOffset,
				   p_output_img->width,
				   p_output_img->height,1,0);
	    }
	}
    }
  *output_colour = is_colour;
}

//Indicate a change in the 
//format of the processing images (=p_src or p_dst)  
int change_it=0;
void ProcImage::process()
{
  int i,flag=0;
  pixel_format temp_type;
  IPLOP *op;
  char struc[] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
  XYDistance distance[128];
  
  //For all operations in the actual processing window
  for (i=0; i<num_ops; i++) 
    {
      if (i>0)
	{ 
	  freeImage(p_src);
	  if((p_src=copyImage(p_dst))==NULL)
	    {
#if DEBUG_IMPROV
	      fprintf(stderr,"Skip Image: p_dst->p_src\n");
#endif
	      printf("Low memory=>\n Skip Image!");
	      flag=1;
	      break;
	    }	
	}
      else
	{
	  //Create p_src
	  if(p_src!=NULL && 
	     p_src->data!=NULL && 
	     p_src->datasize==p_input_img->datasize)
	    {
	      memcpy(p_src->data, p_input_img->data,p_input_img->datasize );
	    }
	  else
	    {
	      freeImage(p_src);
	      if((p_src=copyImage(p_input_img))==NULL)
		{
#if DEBUG_IMPROV
		  fprintf(stderr,"Skip Image: p_input_img->p_src \n");
#endif
		  printf("Low memory=>\n Skip Image!");
		  flag=1;
		  break;
		}
	      //p_src changed -> 
	      //indicate change for p_dst
	      change_it=1;
	    }
	}
      
      //Create p_dst (basically just malloc) without copying any data
      if(p_dst->data==NULL || change_it==1 || num_ops > 1)
	{
	  freeImage(p_dst);
	  p_dst=copyImage_cover(p_src);
	  clearImage(p_dst);
	  change_it=0;
	}
      
      op = &operations[i];
      
      switch (op->operation) 
	{
	  
	case E_Just_Optic_Flow_Camus :
	  {
	    IPL_Just_Optic_Flow_Camus(p_src,
				      op->param[0],op->param[1],op->param[2],
				      op->param[3],op->param[4],op->param[5],
				      p_dst);
	  } break;
	 
	case E_Just_Optic_Flow_Liu :
	  {
	    IPL_Just_Optic_Flow_Liu(p_src,
				    op->param[0],op->param[1]+0.2,op->param[2],
				    op->param[3],op->param[4],op->param[5],
				    p_dst);
	  } break; 
   
	case E_Contour :
	  {
	    if(op->param[0]>0.8)
	      op->param[0]=0.8;
	    
	    IPL_cont_ori(p_src, p_dst,
			 (int) (op->param[0] * 10), 
			 (int) (op->param[1] * 10),
			 (int) (op->param[2] * 10));
	  } break; 
	  
	case E_Negation :
	  {
	    IPL_negation(p_src, p_dst);
	  }  break;
	  
	case E_Identity:
	  IPL_identity(p_src, p_dst);
	  break;
	  
	case E_CamImg :
	  {
	    if(p_dst!=NULL && p_dst->datasize==p_camera_img->datasize)
	      memcpy(p_dst->data,p_camera_img->data,p_camera_img->datasize);
	    else
	      {
		freeImage(p_dst);
		if((p_dst=copyImage(p_camera_img))==NULL)
		  {
		    fprintf(stderr,"Skip Image\n");
		    break;
		  }
	      }
	  }
	  break;
	  
	case E_Dithering :
	  IPL_dither(p_src, p_dst);
	  break;
	  
	case E_Difference :
	  IPL_difference(p_src, p_dst);
	  break;
	  
	case E_Threshold :
	  IPL_threshold(p_src, p_dst, (BYTE)(op->param[0]*255));
	  break;
	  
	case E_ColGray :
	  if(p_src->format==pix_rgb24)
	    IPL_col2gray(p_src, p_dst);
	  else
	    IPL_identity(p_src, p_dst);
	  break;
	  
	case E_MatchCol :
	  IPL_matchcol(p_src, p_dst, (BYTE)(op->param[0]*255),
		       (BYTE)(op->param[1]*255),
		       (BYTE)(op->param[2]*255));
	  break;
	  
	case E_HSV :
	  IPL_HSVValue(p_src, p_dst);
	  break;
	  
	case E_Min :
	  IPL_min(p_src, p_dst);
	  break;
	  
	case E_Max :
	  IPL_max(p_src, p_dst);
	  break;
	  
      case E_Mean :
	IPL_mean(p_src, p_dst);
	break;
	
	case E_Median :
	  IPL_median(p_src, p_dst);
	  break;
	  
	case E_Laplace :
	  IPL_laplace(p_src, p_dst);
	  break;
	  
	case E_Sobel :
	  IPL_sobel(p_src, p_dst);
	  break;
	  
	case E_Corner :
	  IPL_corner(p_src, p_dst);
	break;
	  
	case E_Erosion :
	  IPL_erosion(p_src, p_dst, struc);
	  break;
	  
	case E_Dilation :
	  IPL_dilation(p_src, p_dst, struc);
	break;
	
	case E_Open :
	  IPL_open(p_src, p_dst, struc);
	  break;
	
	case E_Close :
	  IPL_close(p_src, p_dst, struc);
	  break;
	
	case E_Fill :
	  IPL_fill(p_src, p_dst, (int)(op->param[0] * ipl_config.imageWidth),
		   (int)(op->param[1] * ipl_config.imageHeight),
		   struc);
	  is_colour = false;
	  break;
	  
	case E_Connected :
	  IPL_connected(p_src, p_dst, (int)(op->param[0] *
					    ipl_config.imageWidth),
			(int)(op->param[1] *
			      ipl_config.imageHeight),
			struc);
	  is_colour = false;
	  break;
	  
	case E_Boundary :
	  IPL_boundary(p_src, p_dst, struc);
	  is_colour = false;
	  break;
	  
	case E_Skeleton :
	  IPL_skeleton(p_src, p_dst, struc);
	  is_colour = false;
	  break;
	  
	case E_Noise :
	  IPL_noise(p_src, p_dst, op->param[0]);
	  break;
	  
	case E_GrayStretch :
	  IPL_gray_stretch(p_src, p_dst);
	  break;
	  
	case E_GrayReduce :
	  IPL_gray_reduce(p_src, p_dst, (int) (op->param[0]*50));
	  break;
	  
	case E_ShowXY :
	  {
	    IPL_showxy(p_src, p_dst, (int)(op->param[0] *
					   (ipl_config.imageWidth-1)),
		       (int)(op->param[1] *
			     (ipl_config.imageHeight-1)));
	  }
	  break;
	  
	  
      case E_Moravec :
	IPL_moravec(p_src, p_dst);
	is_colour = false;
	break;
	
      case E_Track :
	IPL_Track(p_src, p_dst);
	break;
	
      case E_HistEq :
	IPL_HistEq(p_src, p_dst);
	is_colour = false;
	break;

      case E_Gauss :
	IPL_Gauss(p_src, p_dst);
	is_colour = false;
	break;

	
	case E_Overlay :
	  IPL_Coverlay(p_camera_img, p_input_img, p_output_img,
		       (BYTE)(op->param[0] * 255),
		       (BYTE)(op->param[1] * 255),
		       (BYTE)(op->param[2] * 255));
	  
                  break;
	
	case E_TestPalette :
	  IPL_TestPalette(p_dst);
	  break;
	  
	case E_Region :
	  IPL_region_growing(p_src, p_dst, (BYTE)(op->param[0] *
						  (ipl_config.colorWhite -
						   ipl_config.colorBlack)));
	  is_colour = false;
	  break;
	  
	  //              case E_Circles :
	  //                  IPL_FindCircles(p_src, p_dst, distance);
	  //                  is_colour = false;
	  //                  break;
	  
	  
	case E_Match_Color_Range :
	  IPL_match_color_range(p_src, p_dst,
				(op->param[0]),
				(op->param[1]),
				(op->param[2]));
	  is_colour = false;
	  break;
		
	}
    }
  
  //Produce output picture
  if(num_ops>0 && flag==0)
    {
      //Output picture 
      if(p_dst!=NULL &&
	 p_dst->data!=NULL && 
	 p_output_img->datasize==p_dst->datasize)
	//Properties have not changed => just copy the data!
	memcpy(p_output_img->data, p_dst->data, p_dst->datasize );
      else
	{
	  //Give output picture for next frame
	  //and change the output picture to current format
	  freeImage(p_output_img);
	  if((p_output_img=copyImage(p_dst))==NULL)
	    fprintf(stderr,"Error in creating p_output_img!\n");
	}
    }
  else if(flag==1)
    {
      clearImage(p_output_img);
    }
}

void ProcImage::add_op(IPLOP *op)
{
    const char *text;

    if (num_ops < MAX_IPL_OPS) 
      {
	memcpy(&operations[num_ops], op, sizeof(IPLOP));
	num_ops++;
	text = generate_browser_text(op, browser->size());
	browser->add(text, NULL);
	redraw();
      }
    change_it=1;
}


void ProcImage::remove_op(int n)
{
    int i;
 
    num_ops--;
    
    for (i=n-1; i<num_ops; i++) 
      {
	memcpy(&operations[i], &operations[i+1], sizeof(IPLOP));
      }
    
    browser->deselect(n);
    browser->remove(n);

    browser->middleline(num_ops);

    redraw();
}


void ProcImage::set_op(IPLOP *op, int n)
{
  const char *text;
  
  if (n == 0) 
    {
      add_op(op);
    } 
  else 
    {
      memcpy(&operations[n-1], op, sizeof(IPLOP));
      browser->deselect();
      browser->remove(n);
      text = generate_browser_text(op, n);
      browser->insert(n, text, NULL);
      redraw();
    }
}


void ProcImage::get_op(IPLOP *op, int n)
{
    memcpy(op, &operations[n-1], sizeof(IPLOP));
}


void ProcImage::save_ops(FILE *fp)
{
    fwrite(&num_ops, sizeof(num_ops), 1, fp);
    fwrite(operations, sizeof(operations), 1, fp);
}


void ProcImage::load_ops(FILE *fp)
{
    IPLOP temp[MAX_IPL_OPS];
    int n;
    int i;

    browser->clear();
    num_ops = 0;

    fread(&n, sizeof(num_ops), 1, fp);
    fread(temp, sizeof(temp), 1, fp);

    for (i=0; i<n; i++) {
        add_op(&temp[i]);
    }
}


void ProcImage::menu_cb(Fl_Widget *w, void *param)
{
    Fl_Menu_Button *m = (Fl_Menu_Button *) w;
    ProcImage *p = (ProcImage *) m->parent();
    Improv_Menu_Item *item = (Improv_Menu_Item *) param;
    IPLOP op;
    int n;
    
    printf("\n");

    op.operation = item->operation;
    p->get_params(&op);
    p->get_op_name(item, &op);
    op.num_param = item->num_param;

    n = p->browser->value();
    
    //Indicate start of motion stereo
    if(op.operation==E_Optic_Flow_Camus) 
      count_pic=0;
     
    if(op.operation==E_Optic_Flow_Liu) 
      count_pic_liu=0;
    
    //Indicate start of E_Difference
    if(op.operation==E_Difference)
      buffer=0;
    
    if (n > 0) 
      {
	p->set_op(&op, n);
      }
    else 
      {
	p->set_op(&op, p->browser->size());
      }
    
    p->button->label("+");

    p->set_focus();
}


void ProcImage::button_cb(Fl_Widget *w, void *param)
{
    Fl_Button *b = (Fl_Button *) w;
    ProcImage *p = (ProcImage *) b->parent();
    IPLOP op = {E_Identity, {0.0, 0.0, 0.0}, "Identity", 0};
    int n;
    
    n = p->browser->value();
    if (n > 0) {
      b->label("+");
      p->remove_op(n);
    } 
    else 
      {
	p->add_op(&op);
      }

    p->set_focus();
}


void ProcImage::browser_cb(Fl_Widget *w, void *param)
{
    Fl_Browser *b = (Fl_Browser *) w;
    ProcImage *p = (ProcImage *) b->parent();

    if (b->value() > 0) 
      {
        p->button->label("-");
      } 
    else 
      {
	p->button->label("+");
      }
    p->button->redraw();
    
    p->set_focus();
}


void ProcImage::init_menu()
{
  Improv_Menu_Item *item;
  int menu_size = sizeof(menu_items)/sizeof(Improv_Menu_Item);
  int i;
  
  for (i=0; i<menu_size; i++) {
    item = &menu_items[i];
    menu->add(item->text, 0, ProcImage::menu_cb, item);
    }
}


const char *ProcImage::generate_browser_text(IPLOP *op, int n)
{
  static char text[80];
  
  switch (op->num_param) {
  case 1 :
    sprintf(text, "%s(%1.2g)", op->text, op->param[0]);
    break;
    
  case 2 :
    sprintf(text, "%s(%1.2g,%1.2g)", op->text, 
	    op->param[0],
	    op->param[1]);
    break;
    
  case 3 :
      sprintf(text, "%s(%1.2g,%1.2g,%1.2g)", op->text, 
	      op->param[0],
	      op->param[1],
	      op->param[2]);
      break;
      
  default :
    sprintf(text, "%s", op->text);
    break;
  }
  
    return text;
}


void ProcImage::get_op_name(Improv_Menu_Item *item, IPLOP *op)
{
    const char *start;
    int length;
    
    start = strrchr(item->text, '/');
    
    if (start == NULL) 
      {
	start = item->text;
      } 
    else 
      {
	start++;
      }
    
    if (item->num_param > 0) 
      {
	length = strcspn(start, "(");
	strncpy(op->text, start, length);
	op->text[length] = 0;
      } 
    else 
      {
	strcpy(op->text, start);
      }
}


void ProcImage::get_params(IPLOP *op)
{
    op->param[0] = param[0];
    op->param[1] = param[1];
    op->param[2] = param[2];
    
    //op->param[3] = param[3];
    //op->param[4] = param[4];
    //op->param[5] = param[5];
}

extern int subscale;
void ProcImage::enable_enlarge()
{
    enlarge = false;
    subscale = 0;
}


void ProcImage::disable_enlarge()
{
    enlarge = true;
    subscale = 1;
}


void ProcImage::set_params(double p[])
{
    IPLOP op;
    int n;
    bool selected;

    param[0] = p[0];
    param[1] = p[1];
    param[2] = p[2];
    
    if (have_focus && num_ops > 0) {
        n = browser->value();
        if (n == 0) 
	  {
            n = browser->size();
            selected = false;
	  } 
	else 
	  {
            selected = true;
	  }
        
	get_op(&op, n);

        get_params(&op);

        set_op(&op, n);
        
	if (selected) 
	  {
            browser->select(n);
	  }
    }
}


void ProcImage::save_image(char *fname)
{
  saveImage(fname,p_output_img);
}


int ProcImage::handle(int event)
{
    switch (event) {
        case FL_UNFOCUS :
            have_focus = false;
            browser->deselect();
            button->label("+");
            button->redraw();
//            break;

        default :
            return Fl_Group::handle(event);
    }

    return 1;
}


void ProcImage::set_focus()
{
    double p[3];
    int n;
    IPLOP *op;

    Fl::focus(this);
    have_focus = true;

    if (param_cb != NULL) {
      n = browser->value();

        if (n == 0) {
            if (num_ops == 0) 
	      {
                return;
	      } 
	    else 
	      {
                n = num_ops;
            }
        }

        op = &operations[n-1];

        p[0] = op->param[0];
        p[1] = op->param[1];
        p[2] = op->param[2];
	
        param_cb(p);
    }
}








