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

/**
 * Projet 3A : WAVEATOM transform
 * @author Mahmoud Ghoniem 
 * @date 2007-02-08
 *
 */

/*
 * @file iatom1.cpp
 * @brief Inverse transform of wave atom orthonormal basis
 *
 * 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 fftw1d{
#include "fftw1d.cpp"
}
namespace arithmetic{
#include "arithmetic/pmultcst.cpp"
#include "arithmetic/pdivcst.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 fftw1d{
#include "fftw1d.cpp"
}
namespace arithmetic{
#include "arithmetic/pmultcst.cpp"
#include "arithmetic/pdivcst.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
Img1dsf real(const Img1dC& c){
  int w = c.Width();
  Img1dsf res(w);

  for (int i=0; i<w; i++)
    res[i]=real(c[i]);
  return res;
}

// Extract from a complex image the imaginary parts in a separate images
Img1dsf imag(const Img1dC& c){
  int w = c.Width();
  Img1dsf res(w);

  for (int i=0; i<w; i++)
    res[i]=imag(c[i]);
  return res;
}
// Create a complex image from real images (real and imaginary parts)
Img1dC join (const Img1dsf& re, const Img1dsf& im){
  int w = re.Width();
  Img1dC res(w);

  for (int k=0; k<w; k++)
    res[k] = Complexe(re[k], im[k]);
  return res;
}

/**
 * Function Iatom1
 *
 * @input c : cell array 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]
 *
 * Note:
 * c is a collection of collections of arrays
 * There are nb_scales collections, each one contains nw array
 */
Errc Iatom1 (const Collection& c, Img1dsf& x,char pat, int tp=1)
{
   double A,B,D;
   double Ict, Ifm, Ito,IdxSize;
   int N,H,T;
   int fWidth;

   Img1dsf *empty = new Img1dsf;
 
   Img1dsf *Idx = new Img1dsf;
   Img1dsf *Idx2 = new Img1dsf;

   Img1dsf *res_re = new Img1dsf;
   Img1dsf *res_im = new Img1dsf;

   Img1dsf *f_re = new Img1dsf;
   Img1dsf *f_im = new Img1dsf;

   Img1dsf *if_im = new Img1dsf;

   Img1dC Icf,res,f;

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

   T = c.GETVALUE("size",Ulong);
   N = T;
   H = N/2;
   freq_pat::Freq_pat(lst,H,pat);

   A = N;

   f.New((int)A);

   for (int k=0; k<A; k++)
      f[k]=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];
   char colname[100];

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

      Collection* col;
      sprintf(colname, "collections%d", s);

      col =  (Collection *)(c.GETPOBJECT(colname, Collection));
      nw = col->GETVALUE("nb_packets",Ulong);

      Img1dsf* index;
      index = (Img1dsf *)(col->GETPOBJECT("index_packets", Img1dsf));

      for(unsigned int i=0; i<nw; i++){
	 sprintf(str, "packets%d_%d", s, i);

	 Img1dsf* tab_col = new Img1dsf;

	 if( (*index)[i] == 1 ){
	    tab_col = (Img1dsf *)(col->GETPOBJECT(str, Img1dsf));
	    int tab_size = tab_col->Width();
	       
	    B = 1<<(s-1);// 2^(s-1);
	    D = 2*B;

	    empty->New(tab_size); (*empty) = 0;
	    res_re->New(tab_size);
	    res_im->New(tab_size);

	    fftw1d::FFTW(*tab_col,*empty,*res_re,*res_im,1);

	    arithmetic::PDivCst(*res_re, *res_re, sqrt(tab_size));
	    arithmetic::PDivCst(*res_im, *res_im, sqrt(tab_size));
	    res = join (*res_re, *res_im);

	    Ict = i*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;
	    }

	    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);
	       }
	       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);
	       }

	       for (int k=0; k<IdxSize;k++){
		  int modA,modD;
		  modA = (int)modulo((*Idx)[k],A);
		  modD = (int)modulo((*Idx)[k],D);
		  f[modA] += Icf[k] * res[modD];
	       }
	    }

	    fWidth=f.Width();

	    f_re->New(fWidth);
	    f_im->New(fWidth);
	    if_im->New(fWidth);
	    x.New(fWidth);

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

	    fftw1d::FFTW(*f_re,*f_im, x,*if_im,-1); // (-1) -> ifft
	    arithmetic::PMultCst(x, x, sqrt(fWidth));
	 }
      } //end of nw loop
   }
   return SUCCESS;
}


#ifdef MAIN
#define USAGE   "usage: %s pat(p or q) tp(1 or -1) [im_in|-] [im_out|-]"
#define PARC    2
#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_Collection :{
      Collection* const ims=(Collection*)objs[0];
      objd[0] = new Img1dsf();
      Img1dsf* imd = (Img1dsf*) objd[0];

      result=Iatom1(*ims, *imd, parv[0][0], atoi(parv[1]));
   }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

