/* -*- 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 Jean-Baptiste Hoock - 2005-07-28
 */

/**
 * @file fict.cpp
 *
 * Fast Inverse Curvelet Transform
 *
 * transformee inverse de curvelets rapide
 */

#include <pandore.h>
using namespace pandore;

#include "curvelet.cpp"

#ifdef MAIN
#undef MAIN
#include "frequency/pfftshift.cpp"
#include "fftshift2.cpp"
#define MAIN
#endif

/*
 * Reproduces the initial image after a curvelet transform.
 * This is in fact the adjoint, also the pseudo-inverse
 * The Collection curvelet stores the images of curvelets
 * imd is the result
 */
Errc FICT(const Collection& curvelet, Img2dsf& imd) {
   Long N1,N2;
   Long nbscales;
   Long nbangles_coarse;
   Img1dsl* nbangles;
   Collection** les_curvelets;
   Img2dsl** curv_size;
   
   {//block: recovery of data
      N1=curvelet.GETVALUE("height",Long);
      N2=curvelet.GETVALUE("width",Long);
      nbscales=curvelet.GETVALUE("nbscales",Long);
      nbangles_coarse=curvelet.GETVALUE("nbangles_coarse",Long);
      nbangles=(Img1dsl*)(curvelet.GETPOBJECT("nbangles",Img1dsl));
      les_curvelets=curvelet.GETPARRAY("curvelets",Collection);
      curv_size=curvelet.GETPARRAY("curv_size",Img2dsl);
   }

   //initialization of structures in order to calculate the FICT
   CurveletImage ci(les_curvelets,curv_size,nbangles,nbscales,N1,N2,nbangles_coarse);
   CurveletTransformAlgorithm cta;
   LowPass lp;
  
   Img2dsf lowpass,lowpass_next; //allocations are in functions 
   Img2dsf Xj_re,Xj_im;
   Img2dsf X_re,X_im;
   Xj_re.New(1,1);
   Xj_im.New(1,1);
  
   Double M1, M2; 
   Long bigN1,bigN2;
  
   Long Xj_topleft_1=0;
   Long Xj_topleft_2=0;

   {//block: initialization
      cta.init_Mi_bigNi(N1,N2,M1,M2,bigN1,bigN2);
      X_re.New(bigN1,bigN2);
      X_im.New(bigN1,bigN2);
      X_re=0;
      X_im=0;
      lp.initPasseBas(lowpass,M1,M2,N1,N2);
   }

   {//block recomposition
      PyramidalScaleDecomposition psd2(N1,N2);
      psd2.reconstruction(ci.getCurv(),ci.getNbangles(),lowpass,lowpass_next,
			  Xj_re,Xj_im,X_re,X_im,Xj_topleft_1,Xj_topleft_2);
      M1=psd2.getM1();
      M2=psd2.getM2();
   }

   {
      //variables of loop
      Long columns;
      Long row;
     
      //temporary images
      Img2dsf zero;
      zero.New(X_re.Size());
      zero=0;
      Img2dsf* Y_re=(Img2dsf*)X_re.Clone();
      Img2dsf* Y_im=(Img2dsf*)X_im.Clone();
     
      QuartDeTour(*Y_re,X_re,2);
      QuartDeTour(*Y_im,X_im,2);
      PSub(zero,*Y_im,*Y_im);
     
      //Plus(X_re,*Y_re,X_re);
      //Plus(X_im,*Y_im,X_im);
      for (columns=0; columns<X_re.Width(); columns++)
	 for (row=0; row<X_re.Height(); row++) {
	       X_re[row][columns]+=(*Y_re)[row][columns];
	       X_im[row][columns]+=(*Y_im)[row][columns];
	    }
     
      //desallocation
      zero.Delete();
      Y_re->Delete();
      Y_im->Delete();
   }
  
   {//block coarsest wavelet level 
      //variables of loop
      Long i,j;
      Long loc_1, loc_2; //local variables for loop
      Long h,w; //end of loop (2)
     
      //number of pixels of curvelet
      Long nb_elts; 
      Double racine_nb_elts;
      nb_elts=((ci.getCurv())[nbscales-1][0]).Height()*((ci.getCurv())[nbscales-1][0].Width());
      racine_nb_elts=sqrt((Double)nb_elts);
     
      //temporary images
      Img2dsf zero,temp_Xj_re,temp_Xj_im;
     
      M1/=2;
      M2/=2;
     
      zero.New(((ci.getCurv())[nbscales-1][0]).Size());
      zero=0;
     
      //reallocation of Xj
      Xj_re.Delete();
      Xj_im.Delete();
      Xj_re.New(((ci.getCurv())[nbscales-1][0]).Size());
      Xj_im.New(((ci.getCurv())[nbscales-1][0]).Size());
      temp_Xj_re.New(Xj_re.Size());
      temp_Xj_im.New(Xj_im.Size());
     
      FFTW((ci.getCurv())[nbscales-1][0],zero,Xj_re,Xj_im,1);

      //MultCst(Xj_re,temp_Xj_re,1/racine_nb_elts);
      //MultCst(Xj_im,temp_Xj_im,1/racine_nb_elts);     
      for(i=0; i<Xj_re.Width(); i++)
	 for(j=0; j<Xj_re.Height(); j++) {
	       temp_Xj_re[j][i]=(float)(Xj_re[j][i]*1/racine_nb_elts);
	       temp_Xj_im[j][i]=(float)(Xj_im[j][i]*1/racine_nb_elts);
	    }
     
      FFTShift2(temp_Xj_re,temp_Xj_im,Xj_re,Xj_im);
     
      lp.applyLowPass(Xj_re,lowpass,Xj_re);
      lp.applyLowPass(Xj_im,lowpass,Xj_im);
     
      h=2*(Long)floor(4*M1)+1; 
      w=2*(Long)floor(4*M2)+1;
      for(i=0; i<w; i++) {
	    loc_2=Xj_topleft_2+i;
	    for(j=0; j<h; j++) {
		  loc_1=Xj_topleft_1+j;
		  X_re[loc_1][loc_2]+=Xj_re[j][i];
		  X_im[loc_1][loc_2]+=Xj_im[j][i];
	       }
	 }
     
      //desallocation
      zero.Delete();
      temp_Xj_re.Delete();
      temp_Xj_im.Delete();
   }

   //Block: folding back onto N1-by-N2 matrix
   cta.init_Mi(N1,N2,M1,M2);
   {
      Img2dsf Y_re, Y_im;
      FoldingMatrix fm(N1,N2,M1,M2);
      fm.folding(N1,N2,X_re);
      fm.folding(N1,N2,X_im);
   }
  
   //block: reconstruction of image
   {
      Long i,j; //variables of loop 
      
      Double racine_nb_pixels=sqrt((Double)(X_re.Height()*X_re.Width()));
     
      Img2dsf x_im;
      Img2dsf* cloneX_re=(Img2dsf*) X_re.Clone();
      Img2dsf* cloneX_im=(Img2dsf*) X_im.Clone();
      x_im.New(X_im.Size());
     
      PFFTShift(*cloneX_re,*cloneX_im,X_re,X_im);
      FFTW(X_re,X_im,imd,x_im,-1);
      for(i=0; i<imd.Width(); i++)
	 for(j=0; j<imd.Height(); j++)
	    imd[j][i]*=(float)(1.0/racine_nb_pixels); //normalization
     
      //desallocation
      x_im.Delete();
      cloneX_re->Delete();
      cloneX_im->Delete();
   }

   //desallocation
   X_re.Delete();
   X_im.Delete();  
   Xj_re.Delete();
   Xj_im.Delete();
   lowpass.Delete();
   lowpass_next.Delete();
   return SUCCESS;  
} 

#ifdef MAIN 

#define USAGE "USAGE : %s [-m mask] [col_in|-] [im_out|-]" 
#define PARC 0 // Number of parameters 
#define FINC 1 // Number of input images 
#define FOUTC 1 // Number of output images 
#define MASK 0 // Level of masking 

int main(int argc,char* argv[]) {
   Errc result; // The result code of the execution. 
   Pobject* mask; // The mask. 
   Pobject* objin[FINC+1]; // The input objects; 
   Pobject* objs[FINC+1]; // The source objects masked by the mask. 
   Pobject* objout[FOUTC+1]; // The output object. 
   Pobject* objd[FOUTC+1]; // The result object of the execution. 
   char *parv[PARC+1]; // The input parameters. 
   
   ReadArgs(argc,argv,PARC,FINC,FOUTC,&mask, objin,objs,objout,objd,parv,USAGE,MASK); 
   
   if (objs[0]->Type()==Po_Collection) {
      Collection* const curvelet=(Collection*)objs[0];  
      Long height=curvelet->GETVALUE("height",Long);
      Long width=curvelet->GETVALUE("width",Long);
      objd[0]=new Img2dsf(height,width);
      Img2dsf* const imd=(Img2dsf*)objd[0];
      result=FICT(*curvelet,*imd);  
      
      goto end;
   }
   {
      PrintErrorFormat(objin,FINC);
      result = FAILURE;
   }
   
 end:
   WriteArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,MASK);
   
   Exit(result);
   return 0;
}

#endif
