

#ifndef SCRATCHPAD_H
#define SCRATCHPAD_H

#include "vector.h"
#include "color.h"

#include "vector2d.h"
#include "CoefficientStream.h"
#include "Kernel.h"
#include "Surface.h"


// Define a scratchpad element
typedef struct
{
  COLOR    rad;
  RGB      rgb;
  float    amc;

  union
  {
    VEC2D    dde;      // Used when doing density estimation                 
    COLOR    drc;      // Used when doing radiance-based wavelet compression 
    VECTOR   dd;       // Used when doing the display                        
  } data;

} ScratchPadElement, *ScratchPadElementPtr;



class ScratchPad
{
public:
  
  // Constructs a new ScratchPad.
  // IN:   The maximum recursion level you will ever use with this scratchpad.
  // OUT:  A pointer to a newly allocated scratchpad (NULL if memory allocation failed).
  // POST: The ScratchPad is ready to be used. The content is random.
  ScratchPad(const int r);


  // Destroy the ScratchPad.
  // POST: the memory is released.
  ~ScratchPad();


  // Basic configuration of the ScratchPad.
  // IN:   p is a pointer to the patch we will be working with (display or density estimation)
  //       r is a recursive sudivision level
  // POST: you can use the scratchpadelement array.
  void basicConfigure(PATCH* p,const int r);


  // Configure the Scratchpad for the construction of a new SurfWav2D.
  // IN:   p is a pointer to the patch we want to reconstruct the illumination onto.
  //	   gridsz is the grid size for the mesh
  // POST: you are allowed to call addImpact.
  void configure(PATCH* p,const float gridsz);
  

  // Change the reconstruction kernel type.
  // IN:    a new kernel.
  // POST:  the previous kernel has been deleted. 
  void setKernel(Kernel *k);


  // Change the reconstruction kernel size.
  // IN:   h is the new size of the reconstruction kernel.
  // OUT:  The new size will be taken into account for subsequent impacts.
  void setKernelSize(const float h);
  

  // Set an overall lightning (used for edf for instance)
  // IN:   an additional radiance for every point of the patch.
  //       (if you never call this function it will be assumed to be equal to 0,0,0)
  // POST: this additional radiance will be taken into account for the SurfWav construction
  void setBaseRadiance(const COLOR &base);


  // Add an impact for the construction.
  // IN:   pos    is the position of the impact in parameter space of the patch.
  //       weight is the weight of the reconstruction kernel (w>0).
  // POST: The impact has been taken into account.
  void addImpact(const VEC2D &pos,const COLOR &weight);


  // Construct a SurfWav2D from the ScratchPad contents.
  // IN:   The expected compression ratio.
  // OUT:  A pointer to a SurfWav2D. (NULL if memory allocation failed).
  DiffuseCompressedSurface* constructDiffuseCompressedSurface(const float t);
  DiffuseDecimatedSurface* constructDiffuseDecimatedSurface(const float t);


  // Display a SurfWav.
  // IN:   A SurfWav.
  // POST: The SurfWav is displayed on screen.
  void display(CoefficientStore *);


  // Print some information about the scratchpad.
  // (could be used for debugging purposes )
  void print();


  // Add the contents of an another scratchpad multiplied by a constant to this scratchpad.
  void add(const ScratchPad *toadd,const COLOR& c);

  void writePlyFile(int fd,const float f);

  // HemisphereScratchPad has more control than only the public interface...
  friend class HemisphereScratchPad;


private:
  int                  maxr;        // r = maximum recursion level
  int                  maxnumsub;   // numsub = 2^r
  int                  maxnumline;  // numline = (2^r)+1
  int                  maxnumcoef;  // numline*numline
  
  int                  curr;        // r = actual recursion level
  int                  curnumsub;   // numsub = 2^r
  int                  curnumline;  // numline = (2^r)+1
  int                  curnumcoef;  // numline*numline
  float                curnumsubfp; // floating point numsub

  PATCH*               patch;       // current patch
  int                  issquare;    // 3 or 4 vertices ?
  float                invarea;     // Inverse area of the current patch

  Kernel*              kernel;      // the reconstruction kernel


  ScratchPadElementPtr elems;       // vertex related stuff
  CoefficientStream    coefstream;  // wavelet compression results
  

  
  static const int  maxrefl = 50;            // Maximum number of reflections for one impact.
  VEC2D      refl[maxrefl];                  // Positions of these reflections.
  int        nrefl;                          // Number of reflections.
  int        insideo,insideu,insidev,insidew;// Are the corners inside the kernel ?
  int        reflo,reflu,reflv,reflw;        // How many reflections around a corner ?


  float      scalef;                         // Scale factor
  
  

  int                  *idxs;

  COLOR                curweight;            // Current weigth
  COLOR                accum;                // Accumulated color

  double               hmin,hmax;            // Kernel properties                 
  double               ihs,iht,ihu,ihv,ihw;  // Inverse altitudes length          
  double               ao,au,av,aw;
  double               ls,lt,lu,lv,lw;
  int                  nao,nau,nav,naw;
  VEC2D                cs,ct,cu,cv,cw;       // Edges                              
  VEC2D                ds,dt,du,dv,dw;       // Elementary edges                   
  VEC2D                ns,nt,nu,nv,nw;       // Normalised edges                   
  VEC2D                nns,nnt,nnu,nnv,nnw;  // Opposites normalised edges 




  // Clear the content of the scratchpad.
  // POST: the scratchpad content is zeroed.
  void clear();


  void basicConfigurePatch(PATCH* p);
  void basicConfigureRecursionLevel(const int r);


  void chooseRecursionLevel(const float gridsz);

  // Configure the Scratchpad for the construction of a new triangular SurfWav2D.
  void configureTriangular(const float gridsz);


  // Configure the Scratchpad for the construction of a new quadrilateral SurfWav2D.
  void configureQuadrilateral(const float gridsz);


  // Add an impact for the construction.
  // IN:   pos    is the position of the impact in parameter space of the patch.
  //       weight is the weight of the reconstruction kernel (w>0).
  // POST: The impact has been taken into account.
  void addImpactTriangular(const VEC2D &pos,const COLOR &weight);


  // Add an impact for the construction.
  // IN:   pos    is the position of the impact in parameter space of the patch.
  //       weight is the weight of the reconstruction kernel (w>0).
  // POST: The impact has been taken into account.
  void addImpactQuadrilateral(const VEC2D &pos,const COLOR &weight);
  void addImpactQuadrilateralAux(int u,int v,int goUp);
  int  testAndSetImpact(int idx);


  // Add accumlation parameter to all points
  void addAccum();


  // Reflect an impact around the edges of the triangle.
  void doReflexTriangular(const VEC2D &toreflect,const int forbid);


  // Reflect an impact around the edges of the quadrilateral.
  void doReflexQuadrilateral(const VEC2D &toreflect,const int forbid);


  // Construct a CoefficientStore with the ScratchPad contents
  // IN: The expected compression ratio.
  // OUT: The corresponding coefficient store.
  CoefficientStore* constructCoefficientStore(const float t);


  // Find wavelet scale coefficients
  // POST: Each data.drc field of ScratchPadElement contains a wavelet coefficient.
  void findWaveletCoefficients();


  // Simplify wavelet coefficients
  // IN:   Expected compression ratio.
  // OUT:  The number of remaining coefficients .
  // POST: Some coefficients are set to zero.
  int simplifyWaveletCoefficients(const float t);


  // Iterate a scratchpad in OUT->IN order.
  // IN: function to call at each step (returns 0 to stop)
  void iterateOutIn(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&),int);
  void iterateOutInTriangular(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&),int);
  void iterateOutInQuadrilateral(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&),int);


  // Iterate a scratchpad in IN->OUT order.
  // IN: function to call at each step (returns 0 to stop)
  void iterateInOut(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&));
  void iterateInOutTriangular(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&));
  void iterateInOutQuadrilateral(int (ScratchPad::*functocall)(ScratchPadElement&,ScratchPadElement&,ScratchPadElement&));


  // Iterate a scratchpad in sequential order.
  


  // Helper functions
  int calculateCoefficient(ScratchPadElement &left,ScratchPadElement &right,ScratchPadElement &middle);
  int calculateCoefficient2(ScratchPadElement &left,ScratchPadElement &right,ScratchPadElement &middle);
  int storeCoefficient(ScratchPadElement &left,ScratchPadElement &right,ScratchPadElement &middle);

  void writeCoefficient(ScratchPadElement &left);
};



#endif









