#include "includes.h"
 
 int Quiet=FALSE;
 
/**************************************|****************************************
Routine   : main
Input  par: int argc (argument count)
            char *argv[] (pointer to arguments)
Output par: none
Function  : Main program. Inputs arguments from user. Calls Encoder.
***************************************|***************************************/
 
 void main(int argc,char *argv[])
  {
   char c;
   char *dd,*filename,*cmd;
   int B=2,Delta=4; /* default vals */ 
   int Iterations=6,MaxX=0,MaxY=0,MaxZ=0,NSquare=0,LLevels=0,OffX=0,OffY=0,OffZ=0,snr;
   int MinList=4,MaxList=10,FeatureDimensions=3,GridResolution=1000,i;
   
   LimboHeader *Head=GimmeALimboHeader();
   Parameter   *Par;
   Transformation ****FCCodes;
   
   Par=GimmeAParameter(); /* Initialize vars */
   Par->TMainSub=100;  /* Threshold for main/sub break up */
   Par->TShadeEdge=50; /* Threshold for shade/edge classification */
   Par->TPostProcess=-1; /* Threshold for postprocession */
   Par->XBits=7;  /* X coord bits */
   Par->YBits=7;  /* Y coord bits */
   Par->ZBits=7;  /* Z coord bits */
   Par->SBits=1;  /* mainsub bits - do not change */
   Par->ABits=4;  /* alpha bits */
   Par->AMax=1.0; /* alpha max */
   Par->AMin=0.4; /* alpha min */
   Par->DBits=6;  /* Delta G bits */
   Par->GBits=8;  /* Gray bits - do not change */
   Par->TBits=3;  /* Transformation bits - do not change */
   
   if (argc<3) LearnUsage();
   while(--argc>2)
    {
     c=(*++argv)[0];
     if (!(c=='-')) ErrorHandler(UNKNOWN_OPTION,argv[0]);
     else
      {
       c=(*argv)[1];
       dd= &(*argv)[1];
       switch(c)
        {
         case 'x': MaxX=ReadInteger(&((*argv)[2]));
         break;
         case 'y': MaxY=ReadInteger(&((*argv)[2]));
         break;
         case 'z': MaxZ=ReadInteger(&((*argv)[2]));
         break;
         case 'm': MinList=ReadInteger(&((*argv)[2]));
         break;
         case 's': MaxList=ReadInteger(&((*argv)[2]));
         break;
         case 'b': /* Range blocksize */
          {
           B=ReadInteger(&((*argv)[2]));
           if (B<2) B=2;
          }
         break;
         case 'i': Iterations=ReadInteger(&(*argv)[2]); /* Decoder iterations */
         break;
         case 'l': LLevels=ReadInteger(&(*argv)[2]); /* expand levels */
         break;
         case 'n': NSquare=ReadInteger(&(*argv)[2]); /* n-level square */
         break;
         case 'd': Delta=ReadInteger(&((*argv)[2])); /* delta step */
         break;
         case 'f': FeatureDimensions=ReadInteger(&((*argv)[2]));
         break;
         case 'r': GridResolution=ReadInteger(&((*argv)[2]));
         break;
         case 'q': Quiet=TRUE;
         break;
         case 'A': /* alpha range */ 
          {
           if      (!strncmp("Amin",dd,4)) Par->AMin=ReadFloat(&((*argv)[5]));
           else if (!strncmp("Amax",dd,4)) Par->AMax=ReadFloat(&((*argv)[5]));
           else ErrorHandler(UNKNOWN_OPTION,argv[0]);
           if (Par->AMax>2.55) Par->AMax=2.55;
           if (Par->AMin<0.00) Par->AMin=0.00;
           break;
          }
         case 'Q': /* quant */ 
          {
           if      (!strncmp("Qa",dd,2)) Par->ABits=ReadInteger(&((*argv)[3]));
           else if (!strncmp("Qd",dd,2)) Par->DBits=ReadInteger(&((*argv)[3]));
           else ErrorHandler(UNKNOWN_OPTION,argv[0]);
           break;
          }
         case 'o': /* offsets */ 
          {
           if      (!strncmp("ox",dd,2)) OffX=ReadInteger(&((*argv)[3]));  /* offset x  */
           else if (!strncmp("oy",dd,2)) OffY=ReadInteger(&((*argv)[3]));  /* offset y  */
           else ErrorHandler(UNKNOWN_OPTION,argv[0]);
           break;
          }
         case 'T':  /* Threshold parameters */
          {
           /* main/sub thres */
           if      (!strncmp("Tm",dd,2)) Par->TMainSub =ReadInteger(&((*argv)[3]));
           /* shade/edge thres */
           else if (!strncmp("Te",dd,2)) Par->TShadeEdge=ReadInteger(&((*argv)[3]));
           /* post processing */
           else if (!strncmp("Tp",dd,2)) Par->TPostProcess=ReadInteger(&((*argv)[3]));
           else ErrorHandler(UNKNOWN_OPTION,argv[0]);
           break;
          }
         default: ErrorHandler(UNKNOWN_OPTION,argv[0]);
         break;
        }
      }
    }
   
   cmd = &(*++argv)[0];
   filename = &(*++argv)[0];
   
   c=cmd[0];
   switch(c)
    {
     case 'e': /* encode mode */
      {
       int
       StartBlockSize,DeltaSize,Res=pow((float)GridResolution,1.0/(float)FeatureDimensions)+0.5;
       PoolStructure *Pool;
       BitMap3D *DstImg,*Image;
       clock_t cpu,cpustart;
       
       vprintf(stderr,"Encoding...\n   File:'%s'",filename);
       
       StartBlockSize=B<<NSquare;
       DeltaSize=Delta;
       
       vprintf(stderr,"\nParameters...");
       for (i=NSquare;i>=0;i--) 
       vprintf(stderr,"\n   Level %d:  Domain block size %3d  Range block size %3d  Delta %3d",
               i,B<<(1+i),B<<i,Delta);
       vprintf(stderr,"\n   Threshold for shades/edges:%4d",Par->TShadeEdge);
       vprintf(stderr,"\n   Threshold for main/subs:   %4d",Par->TMainSub);
       vprintf(stderr,"\n   Threshold for postprocess: %4d",Par->TPostProcess);
       vprintf(stderr,"\n   Minimum search list size:  %4d",MinList);
       vprintf(stderr,"\n   Maximum search list size:  %4d",MaxList);
       vprintf(stderr,"\n   Feature space dimensions:  %4d",FeatureDimensions);
       vprintf(stderr,"\n   Feature grid resolution: %6d (%d)",GridResolution,Res);
       
       Image=LoadBitMap3D(filename,MaxX,MaxY,MaxZ,OffX,OffY,2*StartBlockSize,ORGGFX);
       
       Par->XBits=(int)ceil(log((double)(Image->XSize)/Delta)/log(2.0));
       Par->YBits=(int)ceil(log((double)(Image->YSize)/Delta)/log(2.0));
       Par->ZBits=(int)ceil(log((double)(Image->ZSize)/Delta)/log(2.0));
       vprintf(stderr,"\n   X Bit:%d   Y Bit:%d   Z Bit:%d",Par->XBits,Par->YBits,Par->ZBits);
       
       cpustart=clock();
       
       vprintf(stderr,"\nGenerating Domain and Range Pool...");
       
       Pool=FindPool3D(Image,FeatureDimensions,Res,Par->TShadeEdge,NSquare,
                       StartBlockSize,DeltaSize);
       
       cpu=(clock()-cpustart)/CLOCKS_PER_SEC;
       vprintf(stderr,"\n   Pool Generation CPU time used:%d [sec]",cpu);
       
       FCCodes=Encode3D(Image,NSquare,StartBlockSize,Pool,Par,MinList,MaxList);
       
       cpu=(clock()-cpustart)/CLOCKS_PER_SEC;
       vprintf(stderr,"\nCPU time used:%d [sec]",cpu);
       
       DstImg=Decode3D(FCCodes,Image->ImgType,Image->XSize,Image->YSize,Image->ZSize,
                       StartBlockSize,Iterations,0);
       
       snr=100*PSNR3D(Image,DstImg);
       
       Head->HeadSize=(unsigned char)sizeof(LimboHeader)+LIMBOSTRINGSIZE;
       Head->Type=(unsigned char)Image->ImgType;
       Head->MSBx=(unsigned char)(Pool->RangeX*StartBlockSize/256);
       Head->LSBx=(unsigned char)(Pool->RangeX*StartBlockSize-Head->MSBx*256);
       Head->MSBy=(unsigned char)(Pool->RangeY*StartBlockSize/256);
       Head->LSBy=(unsigned char)(Pool->RangeY*StartBlockSize-Head->MSBy*256);
       Head->MSBz=(unsigned char)(Pool->RangeZ*StartBlockSize/256);
       Head->LSBz=(unsigned char)(Pool->RangeZ*StartBlockSize-Head->MSBy*256);
       Head->MSBox=(unsigned char)(OffX/256);
       Head->LSBox=(unsigned char)(OffX-Head->MSBox*256);
       Head->MSBoy=(unsigned char)(OffY/256);
       Head->LSBoy=(unsigned char)(OffY-Head->MSBoy*256);
       Head->MSBoz=(unsigned char)(OffZ/256);
       Head->LSBoz=(unsigned char)(OffZ-Head->MSBoy*256);
       
       Head->Col=(unsigned char)8;
       Head->B=(unsigned char)B;
       Head->Delta=(unsigned char)Delta;
       Head->NSquare=(unsigned char)NSquare;
       
       Head->XBits=Par->XBits;
       Head->YBits=Par->YBits;
       Head->ZBits=Par->ZBits;
       
       if (NSquare) Head->SBits=Par->SBits;
       else Head->SBits=0;
       Head->ABits=Par->ABits;
       Head->AMax=(unsigned char)(Par->AMax*100.0); /* max = 2.55 */
       Head->AMin=(unsigned char)(Par->AMin*100.0);
       Head->DBits=Par->DBits;
       Head->GBits=Par->GBits;
       Head->TBits=Par->TBits;
       
       Head->MSBSNR=(unsigned char)(snr/256);
       Head->LSBSNR=(unsigned char)(snr-Head->MSBSNR*256);
       Head->MSBCPU=(unsigned char)(cpu/256);
       Head->LSBCPU=(unsigned char)(cpu-Head->MSBCPU*256);
       
       SaveFC3D(FCCodes,filename,Head);
       SaveAndView3D(filename,DstImg);
       vprintf(stderr,"\nDone!\n");
      }
     break;
     
     case 'd':  /* decode mode */
      {
       BitMap3D *DstImg;
       Head=LoadFC3D(&FCCodes,filename);
       DstImg=Decode3D(FCCodes,Head->Type,Head->MSBx*256+Head->LSBx,Head->MSBy*256+Head->LSBy,
                       Head->MSBz*256+Head->LSBz,Head->B<<Head->NSquare,Iterations,LLevels);
       SaveAndView3D(filename,DstImg);
       vprintf(stderr,"\nDone!\n");
      }
     break;
     
     case 'v':  /* view fractal header */
      {
       Quiet=FALSE;
       Head=LoadFC3D(&FCCodes,filename);
       HeadInfo3D(FCCodes,Head);
       vprintf(stderr,"\nDone!\n");
      }
     break;
     
     case 'a':  /* analyze fractal code - produce error image, edg, sha images */
      {
       /* 
       Quiet=FALSE;
       BitMapAnalyze3D(filename,Iterations);
       vprintf(stderr,"\nDone!\n");
       */
      }
     break;
     
     case 't':  /* test mode */
      {
       Quiet=FALSE;
       Test(filename);
       vprintf(stderr,"\nDone!\n");
      }
     break;
     
     default : ErrorHandler(UNKNOWN_OPTION,cmd);
     break;
    }
  }
 
 
