/* -*- mode: c++; c-basic-offset: 3 -*-
 *
 * 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
 * @date 2007-05-29
 *
 */

/*
 * @file iatom2.cpp
 * @brief Inverse transform of wave atom orthonormal basis, symmetric version
 *
 * The inverse transformed is computed by reversing the steps
 * of the forward transform.
 */

#include <math.h>
#include <complex>
#include <iostream>
using namespace std;
#include "complexe.h"
using namespace pandore;



#ifdef MAIN
#undef MAIN

namespace freq_pat{
#include "freq_pat.cpp"
}
namespace kf_lf{
#include "kf_lf.cpp"
}
namespace kf_rt{
#include "kf_rt.cpp"
}
namespace fftw{
#include "fftw.cpp"
}
namespace arithmetic{
#include "arithmetic/pmultcst.cpp"
#include "arithmetic/pdivcst.cpp"
}
namespace Utility{
#include "utility/pextractsubimage.cpp"
}
#define MAIN
#else
namespace freq_pat{
#include "freq_pat.cpp"
}
namespace kf_lf{
#include "kf_lf.cpp"
}
namespace kf_rt{
#include "kf_rt.cpp"
}
namespace fftw{
#include "fftw.cpp"
}
namespace arithmetic{
#include "arithmetic/pmultcst.cpp"
#include "arithmetic/pdivcst.cpp"
}
namespace Utility{
#include "utility/pextractsubimage.cpp"
}
#endif

// modulus function
float modulo(float w, float p){ 
  return p!=0 ? w-p*floor(w/p):w;
}

// Extract from a complex image the real parts in a separate images
Img2dsf real(const Img2dC& c){

  Img2dsf res(c.Size());

  for (int i=0; i<c.Width(); i++)
  for (int j=0; j<c.Height(); j++)
    res[i][j] = real(c[i][j]);
  return res;
}

// Extract from a complex image the imaginary parts in a separate images
Img2dsf imag(const Img2dC& c){
  Img2dsf res(c.Size());

  for (int i=0; i<c.Width(); i++)
  for (int j=0; j<c.Height(); j++)
    res[i][j] = imag(c[i][j]);
  return res;
}

// Create a complex image from real images (real and imaginary parts)
Img2dC join (const Img2dsf& re, const Img2dsf& im){
  Img2dC res( re.Size());

  for (int k=0; k<re.Width(); k++)
  for (int l=0; l<re.Height(); l++)
    res[k][l] = Complexe(re[k][l], im[k][l]);
  return res;
}

/**
 * Function Iatom2Sym - Inverse transform of wave atom orthnormal basis, symmetric version
 *
 * @input c  : Image of wave atom coefficients
 * @output x : the reconstructed image

 * @param pat : type of frequency partition which satsifies parabolic scaling relationship
 * @apram tp : type of tranform
 *         1: standard version
 *        -1: Hilbert transformed version
 *         [default set to 1]
 *
 */

Errc Iatom2Sym (Img2dsf& c, Img2dsf& x,char pat, Short tp[2])
{
   float B;
   float Ict, Ifm, Ito, Jct, Jfm, Jto, IdxSize, JdxSize;
   int A,D,N,H;

   // 1D
   Img1dsf *Idx = new Img1dsf;
   Img1dsf *Idx2 = new Img1dsf;

   Img1dsf *Jdx = new Img1dsf;
   Img1dsf *Jdx2 = new Img1dsf;

   Img1dC Icf,Jcf;

   // 2D
   Img2dsf *empty = new Img2dsf;

   Img2dsf *res_re = new Img2dsf;
   Img2dsf *res_im = new Img2dsf;

   Img2dsf *f_re = new Img2dsf;
   Img2dsf *f_im = new Img2dsf;

   Img2dsf *if_im = new Img2dsf;

   Img2dC res,f;
   
   Img2dsf *packet = new Img2dsf;


   //Collection lst;
   Img1duc* lst;
   Ulong nb_scales;
   int nw;

   N = c.Width();
   H = N/2;
   freq_pat::Freq_pat(lst,H,pat);

   A = N;

   f.New(A,A);

   for (int k=0; k<A; k++)
      for (int l=0; l<A; l++)
	 f[k][l]=Complexe(0);

   /**********************/

   // Get the number of scales
   //nb_scales = lst.GETVALUE("nb_scales",Ulong);
   nb_scales = (pat=='p' ? (int) ceil(log2((double)H)/2)+1:(int) floor(log2((double)H)/2)+1);
   //char str[100];

   // Parse the collection c
   // The packets numbering starts at 1 to nb_scales
   for(unsigned int s=1; s<nb_scales+1; s++){
   
      //sprintf(str, "packets%d", s);

      //Img1dsl* tab_lst;
      //tab_lst = (Img1dsl *)(lst.GETPOBJECT(str, Img1dsl));
      //nw = tab_lst->Width();
      nw = lst[s-1].Width();
      
      B = 1<<(s-1);// 2^(s-1);
      D = (int)(2*B);
		  
      empty->New(D,D); 
      res_re->New(D,D);
      res_im->New(D,D);

      for(int i=0; i<nw; i++)
	  for(int j=0; j<nw; j++)
	    {
	       if(lst[s-1][i]==0 && lst[s-1][j]==0){  // leaf 
	       }
	       else{

		  Utility::PExtractSubImage(c, *packet, i*D, j*D, D, D);
		  
		  (*empty) = 0;

		  fftw::FFTWNORM(*packet,*empty,*res_re,*res_im,1);
		  arithmetic::PDivCst(*res_re, *res_re, (float)D);
		  arithmetic::PDivCst(*res_im, *res_im, (float)D);
		  res = join (*res_re, *res_im);

		  Ict = i*B;
		  Jct = j*B;

		  if(i%2==0){
		     Ifm = Ict - 2.0F/3*B;
		     Ito = Ict + 4.0F/3*B;
		  }
		  else{
		     Ifm = Ict - 1.0F/3*B;
		     Ito = Ict + 5.0F/3*B;
		  }

		  if(j%2==0){
		     Jfm = Jct - 2.0F/3*B;
		     Jto = Jct + 4.0F/3*B;
		  }
		  else{
		     Jfm = Jct - 1.0F/3*B;
		     Jto = Jct + 5.0F/3*B;
		  }

		  for(int id=0; id<2; id++){
		     if(id==0){
			IdxSize = floor(Ito)-ceil(Ifm)+1;
			Idx->New((int)IdxSize);
			Idx2->New((int)IdxSize);

			for(int k=0; k<IdxSize; k++){
			   (*Idx)[k] = ceil(Ifm)+k;
			   (*Idx2)[k] = (*Idx)[k]/ B * M_PI;
			}
			Icf = kf_rt::kf_rt(*Idx2, i, tp[0]);
		     }
		     else{
			IdxSize = floor(-Ifm)-ceil(-Ito)+1;
			Idx->New((int)IdxSize);
			Idx2->New((int)IdxSize);

			for(int k=0; k<IdxSize; k++){
			   (*Idx)[k] = ceil(-Ito)+k;
			   (*Idx2)[k] = (*Idx)[k]/ B * M_PI;
			}
			Icf = kf_lf::kf_lf(*Idx2, i, tp[0]);
		     }

		     for(int jd=0; jd<2; jd++){
			if(jd==0){
			   JdxSize = floor(Jto)-ceil(Jfm)+1;

			   Jdx->New((int)JdxSize);
			   Jdx2->New((int)JdxSize);

			   for(int k=0; k<JdxSize; k++){
			      (*Jdx)[k] = ceil(Jfm) + k;
			      (*Jdx2)[k] = (*Jdx)[k] /B * M_PI;
			   }
			   Jcf = kf_rt::kf_rt(*Jdx2, j, tp[1]);
			}
			else{
			   JdxSize = floor(-Jfm)-ceil(-Jto)+1;

			   Jdx->New((int)JdxSize);
			   Jdx2->New((int)JdxSize);

			   for(int k=0; k<JdxSize; k++){
			      (*Jdx)[k] = ceil(-Jto) + k;
			      (*Jdx2)[k] = (*Jdx)[k] / B * M_PI;
			   }
			   Jcf = kf_lf::kf_lf(*Jdx2, j, tp[1]);
			}

			for (int k=0; k<IdxSize;k++)
			   for (int l=0; l<JdxSize; l++){
			      int modDI, modDJ, modAI, modAJ;
			      modDI = (int)modulo((*Idx)[k],D);
			      modDJ = (int)modulo((*Jdx)[l],D);
			      modAI = (int)modulo((*Idx)[k],A);
			      modAJ = (int)modulo((*Jdx)[l],A);
			      f[modAI][modAJ] += Icf[k] * Jcf[l] * res[modDI][modDJ];
			   }
		     }
		  }
		}
	    } //end of nw loop
	    
	    empty->Delete();
	    res_re->Delete();
	    res_im->Delete();
   }

   f_re->New(A,A);
   f_im->New(A,A);
   if_im->New(A,A);
   x.New(A,A);

   (*f_re) = real(f);
   (*f_im) = imag(f);

    fftw::FFTWNORM(*f_re,*f_im, x,*if_im,-1); // (-1) -> ifft
    arithmetic::PMultCst(x, x, (float)A);

   return SUCCESS;
}


#ifdef MAIN
#define USAGE   "usage: %s pat(p or q) tp[0] tp[1] [-m mask] [im_in|-] [im_out|-]"
#define PARC    3
#define FINC    1
#define FOUTC   1
#define MASK    0

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 mask.
   Pobject *objout[FOUTC+1];	// The output object unmasked by mask.
   Pobject *objd[FOUTC+1];	// The result object.
   char *parv[PARC+1];		// The input parameters.
  
   ReadArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,parv,USAGE,MASK);

   switch(objs[0]->Type()){
   case Po_Img2dsf :{
      Img2dsf* const ims=(Img2dsf*)objs[0];
      objd[0] = new Img2dsf();
      Img2dsf* imd = (Img2dsf*) objd[0];

      Short tp[2];
      tp[0] = atoi(parv[1]);
      tp[1] = atoi(parv[2]);

      result=Iatom2Sym(*ims, *imd, parv[0][0], tp);
   }break;
   default :
      PrintErrorFormat(objin,FINC);
      result =FAILURE;
   }
  
   if (result)
      WriteArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,MASK);
   Exit(result);
   return 0;
}

#endif

