/* -*- 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 - 28-07-2005
 * @author Nael Ouédraogo - 05-01-2006
 * @author Yannick Rémy - 05-01-2006
 */

/**
 * @file curvelet.h
 */

#ifndef CURVELET_H
#define CURVELET_H

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

#ifdef MAIN
#define MEMORY_MAIN
#undef MAIN
#endif

#include "arithmetic/pabs.cpp"
#include "arithmetic/pdiv.cpp"
#include "arithmetic/ppow.cpp"
#include "arithmetic/pmult.cpp"
#include "arithmetic/psqrt.cpp"	
#include "arithmetic/padd.cpp"
#include "arithmetic/psub.cpp"
#include "arithmetic/pmultcst.cpp"

#include "utility/pextractsubimage.cpp"

#include "wedgewindow.cpp" 
#include "lowpass.cpp"

#include "fftw.cpp"

#include "quartdetour.cpp"

#ifdef MEMORY_MAIN
#define MAIN
#undef MEMORY_MAIN
#endif

Double my_round(Double);

/**
 * Class CurveletTransformAlgorithm.
 */
class CurveletTransformAlgorithm {
public:
   CurveletTransformAlgorithm();
   ~CurveletTransformAlgorithm();
   
   void init_Mi(const Long&,const Long&,Double&,Double&);
   void init_Mi_bigNi(const Long&,const Long&, Double&,Double&,Long&,Long&);
};

/**
 * Structure of curvelets.
 */
class CurveletImage {
 
private:
   Img2dsf** curv;         //the curvelets
   Img1dsl nbangles;       //number of angles as a function of a scale
   Long nbscales;          //number of scales includind the coarsest wavelet
   //level
   
   //additional information
   Long height;           //height of the original image
   Long width;            //width of the original image
   Long nbangles_coarse;  //number of angles at the 2nd coarsest level,
   //minimum 8, must be a multiple of 4.

public:
   CurveletImage();
   CurveletImage(const Img2dsf&,const Long&,const Long&);
   CurveletImage(Collection**,Img2dsl**,Img1dsl*,const Long&, 
		 const Long& height,const Long&,const Long&);
   ~CurveletImage();
 
public:
   void alloc(const Img1dsl&,const Long&);
   void rechDimMax(const Long&,Long&,Long&);
   //rechDimMax is not used to calculate FCT but to fill the Collection 
   //curvelet

   inline void setImage(const Img2dsf&, Long scale, Long angle);

   //accessors
   Img2dsf** getCurv() {return curv;} 
   Img1dsl& getNbangles() {return nbangles;}
   Long getNbscales() const {return nbscales;} 
   Long getHeight() const {return height;}
   Long getWidth() const {return width;}
   Long getNbangles_coarse() const {return nbangles_coarse;}
};

void CurveletImage::setImage(const Img2dsf& img, Long scale, Long angle) {
   curv[scale][angle].New(img.Size());
   curv[scale][angle]=img;
}

/**
 * Low-pass filter.
 */
class LowPass {
public:
   LowPass();
   ~LowPass();
   
   void initPasseBas(Img2dsf&,const Double&,const Double&, const Long&,const Long&);
   void initHauteFrequence(Img2dsf&,Img2dsf&);
   void reinitXlow(Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&,const Double&,const Double&);
   void applyLowPass(Img2dsf&,Img2dsf&,Img2dsf&);
   void updateXlowXhi(Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&,const Double&,const Double&);
   void initFilters(Img2dsf&,Img2dsf&,const Double&,const Double&);
   void updateX(const Img2dsf&,const Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&, Img2dsf&,Img2dsf&,const Double&,const Double&, const Long&,const Long&);
};

/**
 * pyramidal scale decomposition.
 */
class PyramidalScaleDecomposition {
   Long scale; //the step in the pyramidal decomposition
   //variables M1 and M2 are used for many calculations
   //in the pyramidal decomposition
   //at every step, they are divised by 2
   Double M1;
   Double M2;

public:
   PyramidalScaleDecomposition();
   PyramidalScaleDecomposition(const Long&,const Long&);
   ~PyramidalScaleDecomposition();
   inline void maj_Mi();
   void decompose(const Img1dsl&,Img2dsf**,Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&);
   void reconstruction(Img2dsf**,const Img1dsl&,Img2dsf&,Img2dsf&,Img2dsf&,
		       Img2dsf&,Img2dsf&,Img2dsf&,Long&,Long&);
   //accessors
   Double getM1() {return M1;}
   Double getM2() {return M2;}
};

//update M1 and M2
void PyramidalScaleDecomposition::maj_Mi() {
   M1/=2;
   M2/=2;
}


/**
 * angular decomposition
 */
class AngularDecomposition {
   Long quadrant;         //the step in the decomposition angular
   Long nbquadrants;      //number of steps
   Long nbangles_perquad; //number of angles per quadrant
   //these attributes are used a lot of times. They are used to calculate
   //the size of curvelets
   //M_horiz is a parameter for the height
   //M_vert is a parameter for the width
   //if M_horiz=M1 M_vert=M2 
   //if M_horiz=M2 M_vert=M1
   Double M_horiz;         
   Double M_vert;
   Long theta;            //the step of the orientation 
   
public:
   AngularDecomposition();
   AngularDecomposition(const Img1dsl&,const Long&,const Double&, const Double&);
   AngularDecomposition(const Long&,const Long&,const Long&, const Double&,const Double&,const Long&);
   ~AngularDecomposition();
   
   void maj_M_horiz_M_vert();
   void decompose(Img2dsf&,Img2dsf&,Img2dsf**,const Long&);
   void recompose(Img2dsf** C, Img2dsf& Xj_re, Img2dsf& Xj_im, const Long& scale);
};

/**
 * the class Wedge
 */
class Wedge {
   //friend class
   friend class WedgePartition;
   friend class RegularWedge;
   friend class WedgeSide;
   friend class LeftCornerWedge;
   friend class RightCornerWedge;
  
   //attributes
   Short t_endpoints;            //size of wedge_endpoints
   Img1dsl wedge_endpoints;
   Img1dsf wedge_midpoints;

   //attributes to avoid several times the same operations
   Long abs;
   Long ord;
  
   Long first_wedge_endpoint_vert;
   //a wedge_midpoints corresponds to the middle of 2 following 
   //wedge_endpoints  

public:
   Wedge(Double,Double,Long);
   ~Wedge();
   void afficheWedge();
};

/**
 * Families of WedgePartitions.
 */
class WedgePartition {
protected:
   static Long compteur;  //counts the number of wedge.
   //used to allocate and desallocate static data.
 
   static Wedge* wedge; //the wedge

   static Img2dsl cols; //a matrix of index
   //avoid to repeat some operations

   //geometric caracteristics of wedge
   static Img1dsl left_line; 

   Long subl;
   Double slope_wedge;
   Long width_wedge;
 
   Long length_wedge; 
   //for WedgeSide, this corresponds to length_corner_wedge

public:
   WedgePartition();
   WedgePartition(const Double&,const Double&,const Long&); 
   virtual ~WedgePartition();

   void init_coordX(const Double&,const Img2dsl&,Img2dsf&);
   void calculWedgeWindow(Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&);
   void affiche();

   virtual void decompose(const Img2dsf&,const Img2dsf&,Img2dsf**,
			  const Double&,const Long&,const Long&,Long&)=0;
   virtual void init_wrapped_XX(Img2dsl&)=0;
  
protected:
   virtual void reallocStaticData();
   virtual void initWedgeWindow(Img2dsf&,Img2dsf&,const Double&)=0;
   void init_cols();
   virtual void initLengthWedge(const Double&)=0;
   virtual void initWidthWedge_SlopeWedge_LeftLine()=0;
   void rotation_et_ifft(Img2dsf&,Img2dsf&,Img2dsf&,const Long&);
   void fillCurv(Img2dsf**,Img2dsf&,Img2dsf&,Img2dsf&,Img2dsf&,
		 const Long&,const Long&,const Long&);
   void fillWrappedData(Img2dsf**,Img2dsf&,Img2dsf&,
			const Long&,const Long&,const Long&);
   virtual void unwrappingData(const Img2dsf&,Img2dsf&)=0;

private:
   static void initWedge(const Double&,const Double&,const Long&); 
   static void deleteStaticData();
   static void deleteWedge(); 

   /*
    * for methods to calculate the FICT, the matrix C is normally const
    * but it isn't declared const 
    */
};

class RegularWedge : public WedgePartition {
private:
   Img1dsf mid_line_left_next; //to avoid to make calculations again

public:
   RegularWedge();
   RegularWedge(const Double&,const Double&,const Long&,const Long&);
   RegularWedge(const Double&,const Double&,const Long&);
   ~RegularWedge();
  
   void incrSubl(){subl++;}
   void setSubl(Long v){subl=v;}
  
   void unwrappingData(const Img2dsf&,Img2dsf&);
   void decompose(const Img2dsf&,const Img2dsf&,Img2dsf**,const Double&, 
		  const Long&,const Long&,Long&);
   void recompose(Img2dsf**,Img2dsf&,Img2dsf&, 
		  const Long&,const Long&,const Double&,Long&);
  
private:
   void init_coordX_save_mid_line(const Double&,const Img2dsl&,Img2dsf&);
   void initWedgeWindow(Img2dsf&,Img2dsf&,const Double&);
   void init_wrapped_XX(Img2dsl&);
   void initLengthWedge(const Double&);
   void initWidthWedge_SlopeWedge_LeftLine();
};

class WedgeSide : public WedgePartition { 
protected:
   static Img2dsl admissible_cols; //to avoid some operations 
  
public:
   WedgeSide();
   WedgeSide(const Double&,const Double&,const Long&);
   virtual ~WedgeSide();
  
   virtual void calculAdmissibleCols(const Img1dsl&,Img1dsl&,const Long&)=0;
   virtual void calcul_Ci(Double&,Double&);
   virtual void calcul_coord_corner(const Img2dsl&,const Double&,const Double&,
				    Img2dsf&);

   void decompose(const Img2dsf&,const Img2dsf&,Img2dsf**,
		  const Double&,const Long&,const Long&,Long&);

   void recompose(Img2dsf**,Img2dsf&,Img2dsf&, 
		  const Long&,const Long&,const Double&,Long&);

protected:
   void reallocStaticData();
   void initWedgeWindow(Img2dsf&,Img2dsf&,const Double&);
   void initLengthWedge(const Double&);
   void init_wrapped_XX(Img2dsl&);
   void init_admissible_cols();
};


class LeftCornerWedge : public WedgeSide {
public:
   LeftCornerWedge();
   ~LeftCornerWedge();
   LeftCornerWedge(const Double&,const Double&,const Long&);
   
   void calculAdmissibleCols(const Img1dsl&,Img1dsl&,const Long&);
   void calcul_Ci(Double&,Double&);
   void calcul_coord_corner(const Img2dsl&,const Double&,const Double&, Img2dsf&);
   void unwrappingData(const Img2dsf&,Img2dsf&);

private:
   void initWidthWedge_SlopeWedge_LeftLine();
};

class RightCornerWedge : public WedgeSide {
public:
   RightCornerWedge();
   ~RightCornerWedge();
   RightCornerWedge(const Double&,const Double&,const Long&);
   
   void calculAdmissibleCols(const Img1dsl&,Img1dsl&,const Long&);
   void calcul_Ci(Double&,Double&);
   void calcul_coord_corner(const Img2dsl&,const Double&,const Double&,	Img2dsf&);
   void unwrappingData(const Img2dsf&,Img2dsf&);
   
private:
   void initWidthWedge_SlopeWedge_LeftLine();
};

/**
 * The class FoldingMatrix
 */
class FoldingMatrix {
protected:
   Long shift_1; //shift following the Height
   Long shift_2; //shift following the Width
  
public:
   FoldingMatrix();
   FoldingMatrix(const Long&,const Long&);
   FoldingMatrix(const Long&,const Long&,const Double&,const Double&);
   ~FoldingMatrix();
   void folding(const Long&,const Long&,Img2dsf&);
};

#endif
