#include "ImprovMain.h"
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>

#define DEBUG 0

ImprovUI *improv;

int nr_of_windows=MAX_WINDOWS;

bool stopped = false;
int frame_count = 0;

char img_path[300];
char seq_path[300];

Picture *p_images[MAX_WINDOWS+1];


void param_cb(Fl_Widget *w, void *param)
{
  extern int nr_of_windows;  
  int i;
  double value[3];
    
  value[0] = improv->p1->value();
  value[1] = improv->p2->value();
  value[2] = improv->p3->value();
  
  for(i=0; i<nr_of_windows; i++)
    improv->image2[i]->set_params(value);
}


void cam_param_cb(Fl_Widget *w, void *param)
{
  double value[6];
  
  value[3] = improv->p4->value();
  value[4] = improv->p5->value();
  value[5] = improv->p6->value(); 
  
  improv->image1->set_params(value);
}


void set_window_cb(Fl_Widget *w, void *param)
{ 
  // extern int nr_of_windows;
  // extern int subscale;
  // Fl_Check_Button *b = (Fl_Check_Button *) w;
   
  if (improv->window2->value() == 1) 
    {
      //nr_of_windows=1; 
      //improv = new ImprovUI(nr_of_windows,&ipl_config);
    }
  if (improv->window4->value() == 1) 
    {
      //nr_of_windows=1;
    }
  if (improv->window6->value() == 1) 
    {
      //nr_of_windows=1;
    }
}


void update_param_cb(double p[])
{
  improv->p1->value(p[0]);
  improv->p2->value(p[1]);
  improv->p3->value(p[2]);
}


void quit_cb(Fl_Widget *w, void *param)
{
  improv->main_window->hide();
}


void help_cb(Fl_Widget *w, void *param)
{
  int help_w=465,help_h=350,i;
  
  Fl_Window *help = new Fl_Window(100, 20, help_w, help_h, "IMPROV: Help");
  Fl_Browser *browser = new Fl_Browser(3,3, help_w-3, help_h-3); 
  browser->type(2);
  browser->color(49);
  browser->labelsize(12);
  browser->textsize(12);
  browser->has_scrollbar(2);
  
  for(i = 0; helpText[i] != NULL; i++)
    browser->add(helpText[i], NULL);
  
  help->show();
}


void about_cb(Fl_Widget *w, void *param)
{ int help_w=430,help_h=315,i;
  
  Fl_Window *about = new Fl_Window(250, 20, help_w, help_h, "IMPROV: About");
  Fl_Browser *browser = new Fl_Browser(3,3, help_w-3, help_h-3); 
  browser->type(2);
  browser->color(49);
  browser->labelsize(12);
  browser->textsize(12);
  browser->has_scrollbar(2);
  
  for(i = 0; aboutText[i] != NULL; i++)
    browser->add(aboutText[i], NULL);
  
  about->show();
}


void stop_cb(Fl_Widget *w, void *param)
{ Fl_Light_Button *b = (Fl_Light_Button *) w;

  if (b->value() == 1)
      stopped = true;
  else
  {  stopped = false;
      //        Fl::add_timeout(1.0, timer_cb);
  }
  improv->image1->camera_stopped(stopped);
}


int autobrightness=0;
void auto_cb(Fl_Widget *w, void *param)
{ Fl_Light_Button *b = (Fl_Light_Button *) w;
  
  if (b->value() == 1) 
    autobrightness=1;
  else 
    {
      double value[6];
      
      value[3] = improv->p4->value();
      value[4] = improv->p5->value();
      value[5] = improv->p6->value(); 
      autobrightness=0;  
      improv->image1->set_params(value);
    }
}


void enlarge_cb(Fl_Widget *w, void *param)
{ int i;
  Fl_Light_Button *b = (Fl_Light_Button *) w;
  
  if (b->value() == 1) 
    { improv->image1->enable_enlarge(); 
      for(i=0;i<nr_of_windows;i++)
        improv->image2[i]->enable_enlarge();
    } 
  else 
    { improv->image1->disable_enlarge(); 
      for(i=0;i<nr_of_windows;i++)
	  improv->image2[i]->disable_enlarge();
      improv->image_box->redraw();
    }
}


void save_img_cb(Fl_Widget *w, void *param)
{
  extern int nr_of_windows;
  char image_name[50];
  char no[3];
  int i;
  
  improv->image1->save_image("image1.ppm");
  
  strcpy(image_name, "imagex.ppm");
  
  for(i=0;i<nr_of_windows;i++)
    { 
      sprintf(no, "%d", (i+2));
      image_name[5]=no[0];      
      
      improv->image2[i]->save_image(image_name);
    }
}

#define MAXNAMESZ       4096
char * get_program_path(char * pname)
{
    int         i, j, lg;
    char    *   path;
    static char buf[MAXNAMESZ];

    /* Trivial case: try in CWD */
    sprintf(buf, "./%s", pname) ;
    if (access(buf, X_OK)==0) {
        sprintf(buf, ".");
        return buf ;
    }
    /* Try out in all paths given in the PATH variable */
    buf[0] = 0;
    path = getenv("PATH") ;
    if (path!=NULL) {
        for (i=0; path[i]; ) {
            for (j=i ; (path[j]) && (path[j]!=':') ; j++);
            lg = j - i;
            strncpy(buf, path + i, lg);
            if (lg == 0) buf[lg++] = '.';
            buf[lg++] = '/';
            strcpy(buf + lg, pname);
            if (access(buf, X_OK) == 0) {
                /* Found it! */
                break ;
            }
            buf[0] = 0;
            i = j;
            if (path[i] == ':') i++ ;
        }
    }
    /* If the buffer is still empty, the command was not found */
    if (buf[0] == 0) return NULL ;
    /* Otherwise truncate the command name to yield path only */
    lg = strlen(buf) - 1 ;
    while (buf[lg]!='/') {
        buf[lg]=0 ;
        lg -- ;
    }
    buf[lg] = 0;
    return buf ;
}
#undef MAXNAMESZ


void save_cfg_cb(Fl_Widget *w, void *param)
{ extern int nr_of_windows;
  
  char *fname;
  FILE *fp;
  int magic = IMPROV_MAGIC_NUMBER,i;
    
  fname = fl_file_chooser("Save configuration as...", "*.cfg", "improv.cfg");
  if (fname != NULL)
  { fp = fopen(fname, "wb");
    if (fp == NULL)
    { printf("Can't create file!");
      return;
    }

    // WRITE FILE
    fwrite(&magic, sizeof(magic), 1, fp);
    for(i=0;i<nr_of_windows;i++) improv->image2[i]->save_ops(fp);
    fclose(fp);
    printf("Saved configuration\n");
  }
}


void load_cfg_cb(Fl_Widget *w, void *param)
{
  extern int nr_of_windows;

  char *fname;
  FILE *fp;
  int magic,i;
  
  fname = fl_file_chooser("Load configuration...", "*.cfg", "improv.cfg");
  
    if (fname != NULL) {
        fp = fopen(fname, "rb");
        if (fp == NULL) {
	  printf("Can't open file!\n");
	  return;
        }

        // READ FILE
        fread(&magic, sizeof(magic), 1, fp);
        if (magic != IMPROV_MAGIC_NUMBER) {
            printf("Invalid file format!\n");
        } else {
	  
	  for(i=0;i<nr_of_windows;i++)
	    {
	      improv->image2[i]->load_ops(fp);
	    }
        }

        fclose(fp);

        printf("Loaded configuration\n");
    }
}

/*
 * Return the number of microseconds from timestamp tv1 and tv2
 */
long
timevaldiff(struct timeval *tv1, struct timeval *tv2)
{
  return (tv2->tv_sec - tv1->tv_sec) * 1000000 + (tv2->tv_usec - tv1->tv_usec);
}

void timer_cb(void *)
{
    static float old_rate = 0.0;
    static struct timeval lasttv = {0,0};
    struct timeval tv;
    float new_rate;
    float rate_change;
    static char rate_label[20];
    static char change_label[20];
    
    Fl::add_timeout(1.0, timer_cb);

    if (0 != gettimeofday(&tv, NULL))
      {
	perror("gettimeofday() failed");
	return;
      }
    
    new_rate = frame_count * 1000000.0 / timevaldiff(&lasttv, &tv);
    frame_count = 0;
    lasttv = tv;
    rate_change = new_rate - old_rate;
    if (0 > rate_change) {
        rate_change = -rate_change;
    }
    old_rate = new_rate;
   
    snprintf(rate_label, 20, "FPS: %.2f", new_rate);
    snprintf(change_label, 20, "Change: %.2f", rate_change);
    
    improv->rate->label(rate_label);
    improv->rate_change->label(change_label);
    
    improv->rate->redraw();
    improv->rate_change->redraw();
	
}


extern int restart;
void idle_cb(void *)
{
  extern int nr_of_windows;  
  static bool colour[6];
  int i,j;
  CamInfo info;
  static char dim_label[20];
  static char bpp_label[20];

  if (stopped) 
    return;
  
  if(restart)
    {
      frame_count = 0;
      
      //Free old images
      for(j=0;j<nr_of_windows;j++)
	freeImage(p_images[j]);
      
      //Initiate new images
      for(j=0;j<=nr_of_windows;j++)
	p_images[j]=newImage();
      
      // set Camera image
      improv->image1->initialize(p_images[0], &colour[0]);  // camera image
      
      for(j=0;j<nr_of_windows;j++)
	{
	  improv->image2[j]->initialize(p_images[j], p_images[j+1], 
					p_images[0],
					&colour[j], &colour[j+1], 
					&colour[0]);
	  
	  improv->image2[j]->set_param_cb(update_param_cb);
	}
      
      improv->image1->get_cam_info(&info);
      snprintf(dim_label, 20, "Dim: %dx%d", info.width, info.height);
      snprintf(bpp_label, 20, "BPP: %d", info.bpp);
      improv->dimensions->label(dim_label);
      improv->bpp->label(bpp_label);
      
      improv->dimensions->redraw();
      improv->bpp->redraw();

      restart = 0;
    }
  
  improv->image1->redraw();
  
  for(i=0;i<nr_of_windows;i++)
    improv->image2[i]->redraw();

  improv->text_info->redraw();  
  printf("");
}

void print_usage(void)
{ fprintf(stderr,"\n NAME\n  Improv\n\n DESCRIPTION\n  Image processing tool for robot vision\n\n SYNOPSIS\n  improv [options]\n\n ");
  fprintf(stderr,"OPTIONS\n\n"); 
  fprintf(stderr,"-w             Specify number of windows (2, 4, 6)\n");
  fprintf(stderr,"-q             Using QuickCam\n");
  fprintf(stderr,"-v             Using Video for Linux device\n");
  fprintf(stderr,"-s             Using image sequence\n");       
  fprintf(stderr,"-p[path]       Specify path for sample sequence (e.g ~/user/improv/images/ )\n");
  fprintf(stderr,"               Otherwise look for enviroment variable $IMPROVSEQ\n\n");
}


/* ****************************************************************** */

int main(int argc, char **argv)
{
  static bool colour[6];
  static char dim_label[20];
  static char bpp_label[20];
  extern int which_cam;
  int j;
  CamInfo info;

  int c, oi;
  int errflg;
  extern char *optarg;
  extern int optind, optopt; 

  // set visual
  Fl::visual(FL_RGB);
  
  //Look for enviroment variable 
  if((getenv("IMPROVSEQ"))!=NULL)
    { strcpy(img_path, getenv("IMPROVSEQ"));
      strcat(img_path, "/");
      fprintf(stderr,"Sequence path from $IMPROVSEQ: %s\n",img_path);
    }
  else if(get_program_path("ximprov")!=NULL)
    { strcpy(img_path, get_program_path("ximprov"));
      strcat(img_path, "/images/");
    }
  else 
    strcpy(img_path, "./images/");
  
  //Process arguments
  errflg = 0;
  while ((c = getopt(argc, argv, "hw:p:qvs")) != -1)
   switch (c)
   {
     case 'h':
       print_usage();
       exit(1);
     case 'w':
       oi = atoi(optarg);
       if (DEBUG) fprintf(stderr,"window parameter %d\n", oi);
       if (oi==2 || oi==4 || oi==6) nr_of_windows = oi-1;
         else {errflg++; fprintf(stderr, "window no. must be 2,4,6\n");}
       break;
     case 'p':
       strcpy(img_path, optarg);
       strcat(img_path, "/");
       break;

     case 'q': which_cam=1; 
       fprintf(stderr,"\nUsing QuickCam!\n");
       break;
     case 'v': which_cam=3; 
       fprintf(stderr,"\nUsing Video for Linux device!\n");
       break;
     case 's': which_cam=2; 
       fprintf(stderr,"\nUsing image sequence!\n");
       break;

     case ':':        /* -f or -o without arguments */
       fprintf(stderr, "Option -%c requires an argument\n", optopt);
       errflg++;
       break;
     case '?':
       fprintf(stderr, "Unrecognized option: - %c\n", optopt);
       errflg++;
   }
   if (errflg) { print_usage(); exit(1); }
   if (DEBUG) fprintf(stderr,"seq search path %s \n", img_path);


  ///////////////////////
  // create main window
  ///////////////////////
  improv = new ImprovUI(nr_of_windows,&ipl_config);
  
  //Initiate images
  for(j=0;j<=nr_of_windows;j++)
    p_images[j]=newImage();
  
  // set Camera image
  improv->image1->initialize(p_images[0], &colour[0]);  // camera image
  
  for(j=0;j<nr_of_windows;j++)
  {
    improv->image2[j]->initialize(p_images[j], p_images[j+1], p_images[0],
                                  &colour[j], &colour[j+1], &colour[0]);
    improv->image2[j]->set_param_cb(update_param_cb);
  }

  //get camera attributes and show in IMPROV
  improv->image1->get_cam_info(&info);
  snprintf(dim_label, 20, "Dim: %dx%d", info.width, info.height);
  snprintf(bpp_label, 20, "BPP: %d", info.bpp);
  if (DEBUG) fprintf(stderr, "main: dim %s  bpp %s\n", dim_label, bpp_label);
  improv->dimensions->label(dim_label);
  improv->bpp->label(bpp_label);
  
  // show main window
  improv->show();
  
  Fl::add_timeout(1.0, timer_cb);
  Fl::add_idle(idle_cb, NULL);
  
  Fl::run();
  
  delete improv;
  
  return 0;
}


int printf(const char *format, ...)
{
    static char temp[1024];
    va_list vargs;
    int count;

    va_start(vargs, format);
    count = vsnprintf(temp, 1024, format, vargs);
    va_end(vargs);
   
    improv->text_info->value(temp);
    
    return count;
}

