/* @file pmSubtraction.h
 *
 * PSF-matched image subtraction, based on the Alard & Lupton (1998) and Alard (2000) methods.
 *
 * @author Paul Price, IfA
 * @author GLG, MHPCC
 *
 * @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
 * @date $Date: 2007-12-07 01:57:15 $
 * Copyright 2004-207 Institute for Astronomy, University of Hawaii
 */

#ifndef PM_SUBTRACTION_H
#define PM_SUBTRACTION_H

#include <pslib.h>

#include <pmHDU.h>
#include <pmFPA.h>
#include <pmSubtractionKernels.h>
#include <pmSubtractionStamps.h>

/// @addtogroup imcombine Image Combinations
/// @{

/// Mask values for the subtraction mask
typedef enum {
    PM_SUBTRACTION_MASK_CLEAR       = 0x00, // No masking
    PM_SUBTRACTION_MASK_BAD_1       = 0x01, // Image 1 is bad
    PM_SUBTRACTION_MASK_BAD_2       = 0x02, // Image 2 is bad
    PM_SUBTRACTION_MASK_CONVOLVE_1  = 0x04, // If image 1 is convolved, would be bad
    PM_SUBTRACTION_MASK_CONVOLVE_2  = 0x08, // If image 2 is convolved, would be bad
    PM_SUBTRACTION_MASK_FOOTPRINT_1 = 0x10, // Bad pixel within the stamp footprint of image 1
    PM_SUBTRACTION_MASK_FOOTPRINT_2 = 0x20, // Bad pixel within the stamp footprint of image 2
    PM_SUBTRACTION_MASK_BORDER      = 0x40, // Image border
    PM_SUBTRACTION_MASK_REJ         = 0x80, // Previously tried as a stamp, and rejected
} pmSubtractionMasks;


/// Number of terms in a polynomial
#define PM_SUBTRACTION_POLYTERMS(ORDER) (((ORDER) + 1) * ((ORDER) + 2) / 2)

/// Set the indices for the normalisation and background terms
#define PM_SUBTRACTION_INDICES(NORM,BG,KERNELS) { \
    int numSpatial = PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder); /* Number of spatial terms */ \
    NORM = (KERNELS)->num * numSpatial; \
    BG = NORM + 1; \
}

/// Return the index for the start of the normalisation terms
#define PM_SUBTRACTION_INDEX_NORM(KERNELS) \
    ((KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder))

/// Return the index for the start of the background terms
#define PM_SUBTRACTION_INDEX_BG(KERNELS) \
    (((KERNELS)->num * PM_SUBTRACTION_POLYTERMS((KERNELS)->spatialOrder)) + 1)


/// Convolve the reference stamp with the kernel components
bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, ///< Stamp to convolve
                                const pmSubtractionKernels *kernels, ///< Kernel parameters
                                int footprint ///< Half-size of region over which to calculate equation
    );

/// Reject stamps
int pmSubtractionRejectStamps(pmSubtractionStampList *stamps, ///< Stamps
                              const psVector *deviations, ///< Deviations for each stamp
                              psImage *subMask, ///< Subtraction mask
                              float sigmaRej, ///< Number of RMS deviations above zero at which to reject
                              int footprint ///< Half-size of stamp
    );

/// Generate an image of the convolution kernel
psImage *pmSubtractionKernelImage(const pmSubtractionKernels *kernels, ///< Kernel parameters
                                  float x, float y,///< Normalised position [-1,1] for which to generate image
                                  bool wantDual ///< Calculate for the dual kernel?
                                  );

/// Generate images of the convolution kernel elements
psArray *pmSubtractionKernelSolutions(const psVector *solution, ///< Solution vector
                                      const pmSubtractionKernels *kernels, ///< Kernel parameters
                                      float x, float y ///< Normalised position [-1,1] for images
    );

/// Convolve image in preparation for subtraction
bool pmSubtractionConvolve(pmReadout *out1, ///< Output image 1
                           pmReadout *out2, ///< Output image 2 (DUAL mode only)
                           const pmReadout *ro1, // Input image 1
                           const pmReadout *ro2, // Input image 2
                           const psImage *subMask, ///< Subtraction mask (or NULL)
                           psMaskType blank, ///< Mask value for blank regions
                           const psRegion *region, ///< Region to convolve (or NULL)
                           const pmSubtractionKernels *kernels, ///< Kernel parameters
                           bool useFFT  ///< Use Fast Fourier Transform for the convolution?
    );

/// Generate the convolution of an image, given a precalculated kernel
///
/// The 'image' is a kernel for convenience --- intended to be a stamp
psKernel *p_pmSubtractionConvolveStampPrecalc(const psKernel *image, ///< Image to convolve
                                              const psKernel *kernel ///< Kernel by which to convolve
    );

/// Given (normalised) coordinates (x,y), generate a matrix where the elements (i,j) are x^i * y^j
psImage *p_pmSubtractionPolynomial(psImage *output, ///< Output matrix, or NULL
                                   int spatialOrder, ///< Maximum spatial polynomial order
                                   float x, float y ///< Normalised position of interest, [-1,1]
    );

/// @}
#endif
