/* -*- 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-25-07
 */

/**
 * @file fftw.ccpp
 *
 * FFTW: calculate the Fourier Transform or the inverse Fourier Transform a 
 *       complex image whose dimensions are not only power of 2.
 * re_im$ and im_im$ are real and respectively real and imaginary part of the
 * complex image im$
 * if sign<0, we make the inverse Fourier transform
 *FFTW means The Fastest Fourier Transform in the West
 *
 * FFTW: effectuer la transformee de Fourier ou son inverse sur des images
 *       complexes dont les dimensions ne sont pas forcement des puissances 
 *       de 2.
 * re_im$ et im_im$ designe les parties reelles et imaginaires de im$
 * im$ etant une image complexe
 * si sign<0, on effectue la transformee de Fourier inverse
 *
 * used by FCT and FICT
 *
 * We use a free library written in C by Matteo Frigo and Steven G. Johnson
 * For a documentation in English about the code and the fftw:
 * http://www.fftw.org
 *
 * disadvantage:
 * -when the inverse Fourier transform is calculated, we don't divise 
 *  by the number of pixels. So as to recovery the original imagebafter a fftw
 *  and an "ifftw", we must divise by this number  
 *
 * defaut de la fftw:
 * -quand la ifft est effectuee, on ne divise pas par le nombre de pixels.
 *  Pour retrouver l'image d'origine, il faut donc diviser chaque pixel
 *  par ce nombre.
 */

/**
 * @todo : adapter la transformee aux images 3D
 */

#include <pandore.h>
using namespace pandore;
#include "include_fftw.cpp"

template <typename TYPE>
Errc FFTWNORM(TYPE& ims_re, TYPE& ims_im, Img2dsf& imd_re, Img2dsf& imd_im, Short sign) {
   Long i; //index of data
   Long j,k; //variables of loop
   Long w=ims_re.Width();
   Long h=ims_re.Height();
   Long nb_pixels=w*h;
   fftw_complex* data=new fftw_complex[nb_pixels];
   fftw_complex* out=new fftw_complex[nb_pixels];
   fftwnd_plan plan;
  
   /* initialization */

   //initialization of data
   i=0;
   //order of loops is important
   for (k=0; k<h; k++)
      for (j=0; j<w; j++) {
	 data[i].re=ims_re[k][j];
	 data[i].im=ims_im[k][j];
	 i++;
      }
   
   //initialization of plan
   if (sign<0)
      plan=fftw2d_create_plan(h,w,FFTW_BACKWARD,128);    //ifft
   else
      plan=fftw2d_create_plan(h,w,FFTW_FORWARD,128);   //fft
   
   /* the transformation */
   fftwnd_one(plan,data,out);
   
   /* filling of the output image */
   i=0;
   //order of loops is important
   Float normalization = sign <0 ? (Float)(h*w):1.0F;

   for (k=0; k<h; k++)
      for (j=0; j<w;j++) {
	 imd_re[k][j]=(Float)out[i].re/normalization;
	 imd_im[k][j]=(Float)out[i].im/normalization;
	 i++;
      }
   
   /* desallocation */
   delete data;
   delete out;
   fftwnd_destroy_plan(plan);
   
   return SUCCESS;
}

template <typename TYPE> Errc FFTW(TYPE& ims_re, TYPE& ims_im, Img2dsf& imd_re, Img2dsf& imd_im, Short sign) {
   Long i; //index of data
   Long j,k; //variables of loop
   Long w=ims_re.Width();
   Long h=ims_re.Height();
   Long nb_pixels=w*h;
   fftw_complex* data=new fftw_complex[nb_pixels];
   fftw_complex* out=new fftw_complex[nb_pixels];
   fftwnd_plan plan;
  
   /* initialization */

   //initialization of data
   i=0;
   //order of loops is important
   for (k=0; k<h; k++)
      for (j=0; j<w; j++) {
	 data[i].re=ims_re[k][j];
	 data[i].im=ims_im[k][j];
	 i++;
      }
   
   //initialization of plan
   if (sign<0)
      plan=fftw2d_create_plan(h,w,FFTW_BACKWARD,128);    //ifft
   else
      plan=fftw2d_create_plan(h,w,FFTW_FORWARD,128);   //fft
   
   /* the transformation */
   fftwnd_one(plan,data,out);
   
   /* filling of the out image */
   i=0;
   //order of loops is important
   for (k=0; k<h; k++)
      for (j=0; j<w;j++) {
	 imd_re[k][j]=(Float)out[i].re;
	 imd_im[k][j]=(Float)out[i].im;
	 i++;
      }
   
   /* desallocation */
   delete data;
   delete out;
   fftwnd_destroy_plan(plan);
   
   return SUCCESS;
}

#ifdef MAIN

#define USAGE "USAGE : %s [im_in|-] [im_out|-]" 
#define PARC 1 // Number of parameters 
#define FINC 2 // Number of input images 
#define FOUTC 2 // 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); 

   switch(objs[0]->Type()){ 
   case Po_Img2duc: 
      { 
	 Img2duc* const ims_re=(Img2duc*)objs[0]; 
	 Img2duc* const ims_im=(Img2duc*)objs[1]; 
	 objd[0]=new Img2dsf(ims_re->Size()); 
	 objd[1]=new Img2dsf(ims_im->Size()); 
	 Img2dsf* const imd_re=(Img2dsf*)objd[0]; 
	 Img2dsf* const imd_im=(Img2dsf*)objd[1]; 
	 result=FFTW(*ims_re,*ims_im,*imd_re,*imd_im,(Short)atoi(parv[0])); 
	 break; 
      }
   case Po_Img2dsl:
      {
	 Img2dsl* const ims_re=(Img2dsl*)objs[0]; 
	 Img2dsl* const ims_im=(Img2dsl*)objs[1]; 
	 objd[0]=new Img2dsf(ims_re->Size()); 
	 objd[1]=new Img2dsf(ims_im->Size()); 
	 Img2dsf* const imd_re=(Img2dsf*)objd[0]; 
	 Img2dsf* const imd_im=(Img2dsf*)objd[1]; 
	 result=FFTW(*ims_re,*ims_im,*imd_re,*imd_im,(Short)atoi(parv[0])); 
	 break; 
      } 
   case Po_Img2dsf: 
      { 
	 Img2dsf* const ims_re=(Img2dsf*)objs[0]; 
	 Img2dsf* const ims_im=(Img2dsf*)objs[1]; 
	 objd[0]=new Img2dsf(ims_re->Size()); 
	 objd[1]=new Img2dsf(ims_im->Size()); 
	 Img2dsf* const imd_re=(Img2dsf*)objd[0]; 
	 Img2dsf* const imd_im=(Img2dsf*)objd[1]; 
	 result=FFTW(*ims_re,*ims_im,*imd_re,*imd_im,(Short)atoi(parv[0])); 
	 break; 
      }  
   default: PrintErrorFormat(objin,FINC); 
      result=FAILURE; 
   } 
   WriteArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,MASK);
   Exit(result); 
   return 0;
}

#endif
