#include "defs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "printtext.h"
#include "imageio.h"
#include "sectorlist.h"
#include "colorlist.h"
#include "scale.h"
#include "differ.h"
#include "normalize.h"
#include "blur.h"
#include "resize.h"
#include "enhance.h"
#include "enlight.h"
#include "checkmove.h"
#include "arglist.h"
#ifdef USE_CLUSTER
#include "cluster.h"
#endif

int savecolor (struct colorlist* list,char* colorstring) {
   /*parses a string of an r,g,b:width parameter
     and saves it into the color exclude list*/
   int red,green,blue,width,t,len;
   char *temp,*resultstring;

   if (colorstring==NULL) return -1;
   resultstring=strdup(colorstring);
   temp=resultstring;
   t=0;
   len=strlen(temp);
   while (t<len){
      if (temp[t]==',' || temp[t]==':' || temp[t]==';') {
         len=t;
	 temp[t]='\0';
      }
      t++;
   }
   red=arglist_integer(temp);
   /*Search for the first separator and save the red value*/
   temp=&temp[t];
   t=0;
   len=strlen(temp);
   while (t<len){
      if (temp[t]==',' || temp[t]==':' || temp[t]==';') {
         len=t;
	 temp[t]='\0';
      }
      t++;
   }
   green=arglist_integer(temp);
   /*Search for the 2nd separator and save the green value*/
   temp=&temp[t];
   t=0;
   len=strlen(temp);
   while (t<len){
      if (temp[t]==',' || temp[t]==':' || temp[t]==';') {
         len=t;
	 temp[t]='\0';
      }
      t++;
   }
   blue=arglist_integer(temp);
   /*Search for the 3rd separator and save the blue value*/
   temp=&temp[t];
   width=arglist_integer(temp);
   /*The 4rth value is width and if there are more separators something is wrong*/
   free(resultstring);

   if (width>0) {
      /*This is an excellent method of testing if all args were given, and
        by the way prevents a div/0 in colorlist.c*/
        fprintf(stderr,"Indexed color R:%i G:%i B:%i -- W:%i.\n",red,green,blue,width);
      return(colorlist_addcolor(list,red,green,blue,width));
   }
  fprintf(stderr,"Illegal color specified!\n");
   return -1;
   /*Illegal parameter*/
}

int sectorX (char* sectorstring) {
   /*returns the x coordinate of a specified sectorstring*/
   int t,len;
   char* resultstring;

   if (sectorstring==NULL) return -1;
   resultstring=strdup(sectorstring);
   t=0;
   len=strlen(resultstring);
   while (t<len){
      if (resultstring[t]==',') {
         len=t;
	 resultstring[t]='\0';
      }
      t++;
   }
   t=arglist_integer(resultstring);
   free(resultstring);
   return t;
}

int sectorY (char* sectorstring) {
   /*returns the x coordinate of a specified sectorstring*/
   int t,begin,len;
   char* resultstring;

   if (sectorstring==NULL) return -1;
   resultstring=strdup(sectorstring);
   t=0;
   begin=-1;
   len=strlen(resultstring)+1;
   while (t<len){
      if (begin>-1) {
         if (resultstring[t]==':') {
            len=t;
	    resultstring[t]='\0';
         }
         resultstring[t-begin]=resultstring[t];
      } else if (resultstring[t]==',') {
         begin=t+1;
	 resultstring[t]='\0';
      }
      t++;
   }
   t=arglist_integer(resultstring);
   free(resultstring);
   return t;

}


void printerrorstring(char* title,char *errormessage) {

   if (errormessage!=NULL) fprintf(stderr,"%s: Error: %s!\n\n",title,errormessage);
   fprintf(stderr,"This program will create an image containing\n   the movement sectors between source1 and source2!\n");
   fprintf(stderr,"The Sectorlist will go to stdout, the text to stderr.\n");
   fprintf(stderr,"For machine parsing try \"%s <arguments> 2>/dev/null | <parseprog>\"\n\n",title);
   fprintf(stderr,"Usage: %s [ -s <value> | --sensitivity=<value> ] \\\n",title);
   fprintf(stderr,"        [ --absolute ] \\\n");
   fprintf(stderr,"        [ -e <value> | --enlight=<value> ] \\\n");
   fprintf(stderr,"        [ --blursize=<value> ] \\\n");
   fprintf(stderr,"        [ --resize=<value> ] \\\n");
   fprintf(stderr,"        [ --enhsize=<value> ] \\\n");
   fprintf(stderr,"        [ --enhmul=<value> ] \\\n");
   fprintf(stderr,"        [ --enhqual=<value> ] \\\n");
   fprintf(stderr,"        [ --blur ] [ --no-enhance ] \\\n");
   fprintf(stderr,"        [ --norm ] [ --normsize=<value> ] \\\n");
   fprintf(stderr,"        [ --magnify ] [ --shrink ] \\\n");
   fprintf(stderr,"        [ --color-exclude [<r>,<g>,<b>:<width>] ... --color-end ] \\\n");
   fprintf(stderr,"        [ --exclude [<x>,<y>] [<x>,<y>] ... [<x>,<y>] --end ] \\\n");
   fprintf(stderr,"        [ --sectorsize <value> ] \\\n");
#ifdef USE_CLUSTER
   fprintf(stderr,"        [ -j <threads> ] \\\n");
#endif
   fprintf(stderr,"        <source1> <source2> [<destination>] [--help] [-h]\n\n");
   fprintf(stderr,"    absolute: Use this flag if you want motiontrack just to\n");
   fprintf(stderr,"              test if two images show the same motive\n");
   fprintf(stderr,"              instead of detecting moving objects.\n");
   fprintf(stderr," sensitivity: 0 detect smallest motions, much false alarms.\n");
   fprintf(stderr,"              1 some lesser sensitivity, less flase alarms.\n");
   fprintf(stderr,"              ...\n");
   fprintf(stderr,"              9 detect almost no motion, no false alarms.\n");
   fprintf(stderr,"              default is \"-s 5\"\n");
   fprintf(stderr,"     enlight: Enlightenment factor in %%.\n");
   fprintf(stderr,"              Some camera input must be lighted / darkened.\n");
   fprintf(stderr,"              0 means black, 50 means half the light 200 means doubled.\n");
   fprintf(stderr,"              default is unchanged light which is \"-e 100\"\n");
   fprintf(stderr,"    normsize: averaging size in pixels for normalizing scan.\n");
   fprintf(stderr,"              default is not to normalize.\n");
   fprintf(stderr,"              note that normalizing and color-exclusion lists are\n");
   fprintf(stderr,"              mutually exclusive.\n");
   fprintf(stderr,"    blursize: The brush size for blurring in 1/100 pixels.\n");
   fprintf(stderr,"              default is not to blur at all.\n");
   fprintf(stderr,"      resize: Resize factor in %%. \n");
   fprintf(stderr,"     enhsize: The brush size for edge enhancing in 1/100 pixels.\n");
   fprintf(stderr,"              default is \"--enhsize=400\"\n");
   fprintf(stderr,"      enhmul: Enlightenment factor in %% for edges found in enhance step.\n");
   fprintf(stderr,"              default is \"--enhmul=2000\"\n");
   fprintf(stderr,"     enhqual: Size of the enhance-destination image in %%\n");
   fprintf(stderr,"              default is \"--quality=50\"\n");
   fprintf(stderr,"              (maximum is 100)\n\n");
   fprintf(stderr,"        norm: force normalizing of source images, same as \"--normsize=5\"\n");
   fprintf(stderr,"        blur: force blur step. same as \"--blursize=300\"\n");
   fprintf(stderr,"  no-enhance: force skipping of enhancing step.\n");
   fprintf(stderr,"     magnify: same as \"--resize=200\"\n");
   fprintf(stderr,"      shrink: same as \"--resize=50\"\n");

   fprintf(stderr,"ColorExclude: A list of colors to ignore.\n");
   fprintf(stderr,"              Colors within <width> to this colors in any source\n");
   fprintf(stderr,"              will cause that pixel to be ignored\n");
   fprintf(stderr,"              End the list with --color-end.\n");
   fprintf(stderr,"              default is none\n");
   fprintf(stderr,"     exclude: A list of sectors to ignore.\n");
   fprintf(stderr,"              can be given as [<x>,<y>:<b>] like in the output\n");
   fprintf(stderr,"              End the list with --end.\n");
   fprintf(stderr,"              default is none\n");
   fprintf(stderr,"  sectorsize: The size of a sector, default is 5 (pixel)\n\n");
}

int main (int argc, char *argv[]) {
   ImagePtr imsource1,imsource2,imdest;		/*Working Images*/
   struct colorlist* excludecolors;		/*List of Colors to exclude*/
   struct sectorlist* result;			/*List of Sectors*/
   int answer;					/*Return value*/
   int value,size,enhsize,enhmul,enhqual;
   int normsize,blursize;			/*option values*/
   char* valuechar;				/*Option string*/
   int deltabright,t,sectorsize;		/*check integers*/
   int method;			/*calculation method*/
   struct arglist* carglist;	/*commandline options*/
#ifdef USE_CLUSTER
   int threads;					/*number of threads*/
#endif

   carglist = arglist_new(argc, argv);
   arglist_addarg (carglist,"--help",0);
   arglist_addarg (carglist,"-h",0);
   arglist_addarg (carglist,"--absolute",0);
   arglist_addarg (carglist,"-s",1);
   arglist_addarg (carglist,"--sensitivity",1);
   arglist_addarg (carglist,"-s0",0);
   arglist_addarg (carglist,"-s1",0);
   arglist_addarg (carglist,"-s2",0);
   arglist_addarg (carglist,"-s3",0);
   arglist_addarg (carglist,"-s4",0);
   arglist_addarg (carglist,"-s5",0);
   arglist_addarg (carglist,"-s6",0);
   arglist_addarg (carglist,"-s7",0);
   arglist_addarg (carglist,"-s8",0);
   arglist_addarg (carglist,"-s9",0);
   arglist_addarg (carglist,"-e",1);
   arglist_addarg (carglist,"--enlight",1);
   arglist_addarg (carglist,"--blur",0);
   arglist_addarg (carglist,"--blursize",1);
   arglist_addarg (carglist,"--norm",0);
   arglist_addarg (carglist,"--normsize",1);
   arglist_addarg (carglist,"--resize",1);
   arglist_addarg (carglist,"--enhsize",1);
   arglist_addarg (carglist,"--enhmul",1);
   arglist_addarg (carglist,"--enhqual",1);
   arglist_addarg (carglist,"--no-enhance",0);
   arglist_addarg (carglist,"--magnify",0);
   arglist_addarg (carglist,"--shrink",0);
   arglist_addarg (carglist,"--sectorsize",1);
   arglist_addarg (carglist,"--color-end",0);
   arglist_addarg (carglist,"--end",0);
   arglist_addarg (carglist,"--color-exclude",10000);
   arglist_addarg (carglist,"--exclude",10000);
   arglist_addarg (carglist,"-j",1);

   printwelcomestring();

   if ((arglist_arggiven(carglist,"--help")==0) || (arglist_arggiven(carglist,"-h")==0)) {
      printerrorstring(argv[0],NULL);
      printusagestring();
      return -1;
   }

   if ((arglist_parameter(carglist,"VOIDARGS",3)!=NULL) || (arglist_parameter(carglist,"VOIDARGS",1)==NULL)){
      printerrorstring(argv[0],"Error parsing commandline");
      printusagestring();
      return -1;
   }
   /*Check parameter count*/

#ifdef USE_CLUSTER
   threads=DEFAULT_THREADS;
   if (arglist_arggiven(carglist,"-j")==0) {
      threads=arglist_integer(arglist_parameter(carglist,"-j",0));
   }
   if (threads<1) {
      printerrorstring(argv[0],"Error parsing commandline,\nprocess amount <threads> has to be greater 0.");
      printusagestring();
      return -1;
   }
   /*Check process count */

   cluster_setpnum(threads);
#endif

   excludecolors=colorlist_new();
   t=0;
   if (arglist_arggiven(carglist,"--color-exclude")==0) {
      if (arglist_arggiven(carglist,"--color-end")!=0) {
         printerrorstring(argv[0],"Color-exclude list has no --color-end ");
         printusagestring();
         return -1;
      }
      valuechar=arglist_parameter(carglist,"--color-exclude",t);
      while (valuechar!=NULL) {
         fprintf(stderr,"Excluding color %i -- %s.\n",t,valuechar);
	 savecolor(excludecolors,valuechar);
	 valuechar=arglist_parameter(carglist,"--color-exclude",++t);
      }
   }
   /*Exclude the specified colors*/

   sectorsize=5;
   if (arglist_arggiven(carglist,"--sectorsize")==0) {
      sectorsize=arglist_integer(arglist_parameter(carglist,"--sectorsize",0));
   }
   if (sectorsize<=0) {
      printerrorstring(argv[0],"Error parsing commandline,\nsectorsize <value> has to be any positive integer");
      printusagestring();
      return -1;
   }
   /*Checked for Option sectorsize */

   if ((arglist_arggiven(carglist,"--sensitivity")==0) || (arglist_arggiven(carglist,"-s")==0)) {
      valuechar=arglist_parameter(carglist,"-s",0);
      if (valuechar==NULL) {
         valuechar=arglist_parameter(carglist,"--sensitivity",0);
      }
      value=arglist_integer(valuechar);
      if (value<0 || value>9 ||arglist_isinteger(valuechar)!=0) {
         printerrorstring(argv[0],"Error parsing commandline,\nsensitivity <value> has to be between 0 and 9");
         printusagestring();
         return -1;
      }
   } else {
      value=5;
   }
   if (arglist_arggiven(carglist,"-s0")==0) value=0;
   if (arglist_arggiven(carglist,"-s1")==0) value=1;
   if (arglist_arggiven(carglist,"-s2")==0) value=2;
   if (arglist_arggiven(carglist,"-s3")==0) value=3;
   if (arglist_arggiven(carglist,"-s4")==0) value=4;
   if (arglist_arggiven(carglist,"-s5")==0) value=5;
   if (arglist_arggiven(carglist,"-s6")==0) value=6;
   if (arglist_arggiven(carglist,"-s7")==0) value=7;
   if (arglist_arggiven(carglist,"-s8")==0) value=8;
   if (arglist_arggiven(carglist,"-s9")==0) value=9;
   /*Checked for option sensitivity*/

   deltabright=25;
   /*default value for sensitivity*/
   if (value==0) deltabright=7;
   if (value==1) deltabright=9;
   if (value==2) deltabright=12;
   if (value==3) deltabright=16;
   if (value==4) deltabright=20;
   if (value==5) deltabright=25;
   if (value==6) deltabright=32;
   if (value==7) deltabright=40;
   if (value==8) deltabright=50;
   if (value==9) deltabright=64;

   if ((arglist_arggiven(carglist,"--enlight")==0) || (arglist_arggiven(carglist,"-e")==0)) {
      valuechar=arglist_parameter(carglist,"-e",0);
      if (valuechar==NULL) {
         valuechar=arglist_parameter(carglist,"--enlight",0);
      }
      value=arglist_integer(valuechar);
      if (value<=0) {
         printerrorstring(argv[0],"Error parsing commandline,\n enlight <value> has to be any positive interger");
         printusagestring();
         return -1;
      }
      value=arglist_integer(valuechar);

      
   } else {
      value=100;
   }
   /*Checked for option enlight*/

   normsize=0;
   if (arglist_arggiven(carglist,"--norm")==0) {
      normsize=5;
   }
   if (arglist_arggiven(carglist,"--normsize")==0) {
      normsize=arglist_integer(arglist_parameter(carglist,"--normsize",0));
      if (normsize<=0) {
         printerrorstring(argv[0],"Error parsing commandline,\nnormsize <value> has to be any positive integer");
         printusagestring();
         return -1;
      }
   }
   /*Checked for Option normsize */

   blursize=0;
   if (arglist_arggiven(carglist,"--blur")==0) {
      blursize=300;
   }
   if (arglist_arggiven(carglist,"--blursize")==0) {
      blursize=arglist_integer(arglist_parameter(carglist,"--blursize",0));
      if (blursize<=0) {
         printerrorstring(argv[0],"Error parsing commandline,\nblursize <value> has to be any positive integer");
         printusagestring();
         return -1;
      }
   }
   /*Checked for Option blursize */

   size=100;
   if (arglist_arggiven(carglist,"--resize")==0) {
      size=arglist_integer(arglist_parameter(carglist,"--resize",0));
   }
   if (size<=0) {
      printerrorstring(argv[0],"Error parsing commandline,\nresize <value> has to be any positive integer");
      printusagestring();
      return -1;
   }
   /*Checked for Option resize */

   enhsize=400;
   if (arglist_arggiven(carglist,"--enhsize")==0) {
      enhsize=arglist_integer(arglist_parameter(carglist,"--enhsize",0));
   }
   if (enhsize<=0) {
      printerrorstring(argv[0],"Error parsing commandline,\nenhsize <value> has to be any positive integer");
      printusagestring();
      return -1;
   }
   if (arglist_arggiven(carglist,"--no-enhance")==0) {
      enhsize=0;
   }
   /*Checked for Option enhsize */

   enhmul=2000;
   if (arglist_arggiven(carglist,"--enhmul")==0) {
      enhmul=arglist_integer(arglist_parameter(carglist,"--enhmul",0));
   }
   if (enhmul<=0) {
      printerrorstring(argv[0],"Error parsing commandline,\nenhmul <value> has to be any positive integer");
      printusagestring();
      return -1;
   }
   /*Checked for Option enhmul */

   enhqual=50;
   if (arglist_arggiven(carglist,"--enhqual")==0) {
      enhqual=arglist_integer(arglist_parameter(carglist,"--enhqual",0));
   }
   if (enhqual<=0) {
      printerrorstring(argv[0],"Error parsing commandline,\nenhqual <value> has to be any positive integer");
      printusagestring();
      return -1;
   }
   /*Checked for Option enhqual */

   

   imsource1=loadim(arglist_parameter(carglist,"VOIDARGS",0));
   if (imsource1==NULL) {
      printerrorstring(argv[0],"Error loading source image 1");
      printusagestring();
      return -1;
   }
   /*Load Sourcefile1*/

   imsource2=loadim(arglist_parameter(carglist,"VOIDARGS",1));
   if (imsource2==NULL) {
      destroyim(imsource1);
      printerrorstring(argv[0],"Error loading source image 2");
      printusagestring();
      return -1;
   }
   /*Load Sourcefile2*/

   if (normsize!=0) {
      normalize(&imdest,imsource1,normsize);
      destroyim(imsource1);
      imsource1=imdest;
      normalize(&imdest,imsource2,normsize);
      destroyim(imsource2);
      imsource2=imdest;
   }
   /*normalize source images if needed*/

   differ(&imdest,imsource1,imsource2,excludecolors);
   /*Do the main work in differ.h*/

   destroyim(imsource1);
   destroyim(imsource2);
   imsource1=imdest;
   /*Free unused image memory*/

   if (value!=100) {
      enlight(&imdest,imsource1,value);
      /*Do the main stuff in enlight.h */

      destroyim(imsource1);
      imsource1=imdest;
      /*Free unused image memory*/
   }

   if (blursize!=0) {
      blur(&imdest,imsource1,blursize);
      /*Do the main stuff in enlight.h */

      destroyim(imsource1);
      imsource1=imdest;
      /*Free unused image memory*/
   }

   if (size!=100) {
      resize(&imdest,imsource1,size),
      /*Do the main stuff in resize.h */

      destroyim(imsource1);
      imsource1=imdest;
      /*Free unused image memory*/
   }

   if (enhsize!=0) {
      enhance(&imdest,imsource1,enhsize,enhmul,enhqual),
      /*Do the main stuff in enhance.h */

      destroyim(imsource1);
      imsource1=imdest;
      /*Free unused image memory*/
   }

   if (arglist_arggiven(carglist,"--absolute")==0) {
      method=1;
      deltabright*=3;
   } else {
      method=0;
   } 
   result=checkmove(&imdest,imsource1,deltabright,sectorsize,method);
   /*Do the movement check*/

   t=0;
   if (arglist_arggiven(carglist,"--exclude")==0) {
      if (arglist_arggiven(carglist,"--end")!=0) {
         printerrorstring(argv[0],"Exclude list has no --end ");
         printusagestring();
         return -1;
      }
      valuechar=arglist_parameter(carglist,"--exclude",t);
      while (valuechar!=NULL) {
         fprintf(stderr,"Excluding sector %i -- %s.\n",t,valuechar);
	 fprintf(stderr,"Xcoord = %i, Ycoord = %i\n",sectorX(valuechar),sectorY(valuechar));
         sectorlist_remsector(result,sectorX(valuechar),sectorY(valuechar));
         valuechar=arglist_parameter(carglist,"--exclude",++t);
      }
   }
   /*Exclude the specified exclusion sectors*/

   if (arglist_parameter(carglist,"VOIDARGS",2)!=NULL) {
      if (saveim(imdest,arglist_parameter(carglist,"VOIDARGS",2))!=0) {
         printerrorstring(argv[0],"Error saving destination image");
      }
      /*Save the resulting image*/
   }

   destroyim(imsource1);
   destroyim(imdest);
   /*We free our memory*/

   answer=sectorlist_print(result);
   /*give back what we have*/

   sectorlist_kill(result);
   /*free the memory of our sectorlist manually*/

   arglist_kill(carglist);
   /*i forgot to kill the commandline argument list*/

   return answer;
}
