/* -*- mode: c++; c-basic-offset: 3 -*-
 *
 * PANDORE (PANTHEON Project)
 *
 * GREYC IMAGE
 * 6 Boulevard Maréchal Juin
 * F-14050 Caen Cedex France
 *
 * This file is free software. You can use it, distribute it
 * and/or modify it. However, the entire risk to the quality
 * and performance of this program is with you.
 *
 * Please email any bugs, comments, and/or additions to this file to:
 * pandore@greyc.ensicaen.fr
 * http://www.greyc.ensicaen.fr/EquipeImage/Pandore/
 */

/**
 * @author Jalal Fadili - 18-03-2005
 */

/**
 * @file dictionary.cpp
 *
 *  dictionary objects: NameOfDict, par1, par2, par3
 *
 * (1) A dictionary is specified by four variables: 
 *		NameOfDict, par1, par2, par3
 *     where NameOfDict is a string, the name of the dictionary. par1-3
 *     are three paramters of the dictionary; they can be numbers or vectors.
 *
 * (2) The implemented dictionaries have at most three parameters.
 *     The unused par's can be given any value, e.g., 0.
 *	o Discrete Cosine Transform
 *		NameOfDict = 'DCT'
 *		no parameter
 *	o Discrete Sine Transform
 *		NameOfDict = 'DST'
 *		no parameter
 *	o Periodized-Orthogonal Wavelet
 *		NameOfDict = 'DWT'
 *		par1 = L, coarsest level
 *		par2 = qmf
 *	o Undecimated A trou Wavelet transform
 *		NameOfDict = 'UDWT'
 *		par1 = L, coarsest level
 *		par2 = qmf
 *	o Isotropic Undecimated Wavelet Transform (B cubic spline)
 *		NameOfDict = 'ISOUDWT'
 *		par1 = L, coarsest level
 *	o Local DCT IV transform (Cosine Packet at a fixed depth with the sine bell)
 *		NameOfDict = 'LDCTIV'
 *		par1 = window width
 *	o Local DCT transform
 *		NameOfDict = 'LDCT'
 *		par1 = window width
 *	o Local DST transform
 *		NameOfDict = 'LDST'
 *		par1 = window width
 *	o Discrete Fast Curvelet Transform
 *		NameOfDict = 'FCT'
 *		par1 = scale
 *		par2 = number of orientations at coarsest scale (16 is default)
 *
 * (3) Merging dictionaries:
 *     One can merge any of the implemented dictionaries to form
 *     a new mega dictionary by using dictionary.cc. 
 *	e.g.
 *		dictionary UDWT L  qmf.col 0 dict1.col
 *		dictionary LDCT 16 0 	   0 dict1.col dict1.col
 *	   
 *	the merged dictionary contains both the UDWT and the LDCT transforms.
 *	The dictionary is represented as a Pandore collection object.
 *
 */

#ifdef _MSC_VER
#include <io.h>
#endif

#include <pandore.h>
using namespace pandore;
#include <collection.h>

#define NB_ANGLES_COARSE 16 // For FCT
#define NumberOfDictionaries 10
char* AvailableDictionaries[NumberOfDictionaries] = {"DWT","UDWT","ISOUDWT","DCT",
						     "DST","LDCT","LDST","LDCTIV",
						     "DCTIV","FCT"};

#ifdef MAIN
#define USAGE   "USAGE : dictionary nameOfdict par1 par2 par3 col_in col_out\n"
void usage(char* argv[]) {
   std::cout<<"\n\t\tBuild a dictionary object collection (structure) by merging several transforms.\n"<<std::endl;

   std::cerr<<USAGE<<std::endl;
   std::cout<<"ALL ARGUMENTS ARE MANDATORY."<<std::endl;

   std::cout<<"\nDescription and Inputs:"<<std::endl;
   std::cout<<"\t(1) A dictionary is specified by four variables:"<<std::endl;
   std::cout<<"\t\tNameOfDict, par1, par2, par3"<<std::endl;
   std::cout<<"\t\twhere NameOfDict is a string, the name of the dictionary. par1-3"<<std::endl;
   std::cout<<"\t\tare three paramters of the dictionary; they can be numbers or vectors."<<std::endl;

   std::cout<<"\t(2) The implemented dictionaries have at most three parameters."<<std::endl;
   std::cout<<"\t\tThe unused par's can be given any value, e.g., 0."<<std::endl;
   std::cout<<"\t\to Discrete Cosine Transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'DCT'"<<std::endl;
   std::cout<<"\t\t\tno parameter"<<std::endl;
   std::cout<<"\t\to Discrete Sine Transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'DST'"<<std::endl;
   std::cout<<"\t\t\tno parameter"<<std::endl;
   std::cout<<"\t\to Periodized-Orthogonal Wavelet"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'DWT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = L, coarsest level"<<std::endl;
   std::cout<<"\t\t\tpar2 = qmf collection"<<std::endl;
   std::cout<<"\t\to Undecimated A trou Wavelet transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'UDWT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = L, coarsest level"<<std::endl;
   std::cout<<"\t\t\tpar2 = qmf collection"<<std::endl;
   std::cout<<"\t\to Isotropic Undecimated Wavelet Transform (B cubic spline)"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'ISOUDWT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = L, coarsest level"<<std::endl;
   std::cout<<"\t\to Fast curvelet transform (not available yet, coming shortly)"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'FCT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = L, coarsest level"<<std::endl;
   std::cout<<"\t\to Local DCT IV transform (Cosine Packet at a fixed depth with the sine bell)"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'LDCTIV'"<<std::endl;
   std::cout<<"\t\t\tpar1 = window width"<<std::endl;
   std::cout<<"\t\to Local DCT transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'LDCT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = window width"<<std::endl;
   std::cout<<"\t\to Local DST transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'LDST'"<<std::endl;
   std::cout<<"\t\t\tpar1 = window width"<<std::endl;
   std::cout<<"\t\to Discrete Fast Curvelet Transform"<<std::endl;
   std::cout<<"\t\t\tNameOfDict = 'FCT'"<<std::endl;
   std::cout<<"\t\t\tpar1 = scale"<<std::endl;
   std::cout<<"\t\t\tpar1 = nb wedges at coarsest scale (typically 16)"<<std::endl;

   std::cout<<"\t(3) Merging dictionaries:"<<std::endl;
   std::cout<<"\t\t\tOne can merge any of the implemented dictionaries to form"<<std::endl;
   std::cout<<"\t\t\ta new mega dictionary by using ./dictionary."<<std::endl;

   std::cout<<"\nOutput:"<<std::endl;
   std::cout<<"\tcol_out : the dictionary Pandore collection file."<<std::endl;
   std::cout<<"\tIf the input dictionary col_in exists, then col_out is a concatenation of col_in and the new transform. Otherwise col_out is ignored and the dictionary is stored in col_in."<<std::endl;

   std::cout<<"\nExample:"<<std::endl;
   std::cout<<"\tdictionary UDWT 5 qmf.col 0 dict1.col dict1.col"<<std::endl;
   std::cout<<"\t(this creates dict1.col containing the UDWT transform (qmf.col can be created by qmf))."<<std::endl;
   std::cout<<"\tdictionary LDCT 16 0 0 dict1.col dict1.col"<<std::endl;
   std::cout<<"\t(the merged dictionary contains both the UDWT and the LDCT transforms)."<<std::endl;
}
#define PARC 6

int main(int argc, char* argv[]) {
   Errc result;              // The result code of the execution.
   Pobject *objin[3];   	    // The input objects;
   int i, size;
   Collection** newdict=new Collection*;
   Collection* mergedict, **tabdict, **oldict;
   Float par1, *par2, par3;
  
   // Check the number of arguments or -h option.
   // Print USAGE.
   if(argc<=PARC || ((argc>=2)&&(!strcmp(argv[1],"-h")))){
      usage(argv);
      exit(FAILURE);
   }
	
   // Check if the dictionary is available.
   for(i = 0;i < NumberOfDictionaries; i++)
      if(!strcmp(argv[1],AvailableDictionaries[i])) break;

   if(i==NumberOfDictionaries){
      std::cerr<<"No dictionary "<<argv[1]<<" implemented yet within the SparseRep pandore toolbox."<<std::endl;
      exit(FAILURE);
   }
	
   char* dict = argv[1];
	
   // Scan the dictionary parameters.
   if((float)atoi(argv[2]) != atof(argv[2])){
      std::cerr<<"The 1st parameter of the dictionary must be an integer."<<std::endl;
      exit(FAILURE);
   }
	
   par1 = (Float)atof(argv[2]);
	
   // The wavelet-based dictionaries could require QMF filters.
   if(!strcmp(dict,"DWT") || !strcmp(dict,"UDWT")){
      objin[0]=LoadFile(argv[3]);
      if (objin[0] == NULL){
	 std::cerr<<"We cannot load the qmf."<<std::endl;
	 delete newdict;
	 Exit(FAILURE);
      }
      if(objin[0]->Type()!=Po_Collection){
	 std::cerr<<"For DWT or UDWT dictionaries the 2nd argument must be a QMF collection."<<std::endl;
	 delete newdict;
	 Exit(FAILURE);
      }
      Collection* const c = (Collection*)objin[0];
      char * nameqmf  = c->GETARRAY("filtre", Char);
      par2 = c->GETARRAY(nameqmf, Float);
      size = c->GETARRAYSIZE(nameqmf, Float);
   }
   else {	
      par2=new float[1];
      if(!strcmp(dict,"FCT") && atoi(argv[3])>32) 
	 par2[0]=NB_ANGLES_COARSE;
      else
	 par2[0]=(Float)atof(argv[3]);
      size = 1;
   }
	
   // Scan the dictionary parameters.
   if((float)atoi(argv[4]) != atof(argv[4])){
      std::cerr<<"The 3rd parameter of the dictionary must be an integer."<<std::endl;
      delete newdict;
      exit(FAILURE);
   }
	
   par3 = (float)atof(argv[4]);
	
   newdict[0] = new Collection;
   newdict[0]->SETARRAY("NameOfDict",Char,dict,strlen(dict)+1);
   newdict[0]->SETVALUE("PAR1",Float,par1);
   newdict[0]->SETARRAY("PAR2",Float,par2,size);
   newdict[0]->SETVALUE("PAR3",Float,par3);
		
   // Check existence of the collection file.
#ifndef _MSC_VER
   if (access(argv[5], F_OK)!=0 || argc == PARC){
#else
   if (_access(argv[5],00)!=0 || argc == PARC){
#endif
      mergedict=new Collection;
      mergedict->SETPARRAY("Dict",Collection,newdict,1);
      mergedict->SaveFile(argv[5]);
      delete mergedict;
      delete newdict[0];
      delete newdict;
      result=SUCCESS;
      goto end;
   } else {	
      // Otherwise, load old dictionary and merge the new one.
      objin[1]=LoadFile(argv[5]);
      if (objin[1] == NULL){
	 delete newdict[0];
	 delete newdict;
	 Exit(FAILURE);
      }
      if(objin[1]->Type()!=Po_Collection){
	 std::cerr<<"The input file is not a collection."<<std::endl;
	 delete newdict[0];
	 delete newdict;
	 Exit(FAILURE);
      }
      mergedict = (Collection*)objin[1];
      
      Long nbofdict=mergedict->GETPARRAYSIZE("Dict",Collection);
      oldict=mergedict->GETPARRAY("Dict",Collection);
      tabdict = new Collection*[nbofdict+1];
      
      // Copy the old dictionary.
      for(i=0;i<nbofdict;i++)
	 tabdict[i]=oldict[i];
      
      // Add the last one.	
      tabdict[i]=*newdict;
      
#ifdef DEBUG
      for(i=0;i<=nbofdict;i++){
	 dict=tabdict[i]->GETARRAY("NameOfDict",Char);
	 par1=tabdict[i]->GETVALUE("PAR1",Float);
	 par2=tabdict[i]->GETARRAY("PAR2",Float);
	 size=tabdict[i]->GETARRAYSIZE("PAR2", Float);
	 par3=tabdict[i]->GETVALUE("PAR3",Float);
	 std::cout<<dict<<" "<<par1<<" "<<par2[0]<<" "<<size<<" "<<par3<<std::endl;
      }
#endif
      // Save the new dictionary.
      mergedict=new Collection;
      mergedict->SETPARRAY("Dict",Collection,tabdict,nbofdict+1);
      mergedict->SaveFile(argv[6]);
      delete tabdict;
      delete mergedict;
      delete newdict[0];
      delete newdict;
      result=SUCCESS;
   }
   
 end:
   Exit(result); 
   return 0;
}

#endif
