#if !defined(POIS_H)
#define POIS_H

#include "pslib.h"

#define abs(x) ((x) >= 0 ? (x) : (-(x)))

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

/* Configuration options */
typedef struct {
    int verbose;			// Verbosity level
    char *refFile;			// Reference image
    char *inFile;			// Input image filename
    char *outFile;			// Output convolved image filename
    int xKernel, yKernel;		// Half the size of the kernel in x and y
    int xImage, yImage;			// Size of the images in x and y
    float threshold;			// Threshold for pixel to include in calculation
    int footprint;			// Footprint size for stamp
    int nsx, nsy;			// Number of stamps in x and y
    float refSat, inSat;		// Saturation values for reference and input images
    float refBad, inBad;		// Bad values for reference and input images
    float background;			// Value to add to the background in images to get above zero
    int spatialOrder;			// Order of spatial variations in the kernel
    int numIter;			// Number of rejection iterations
    float sigmaRej;			// Limit (in std dev) for rejection
    float penalty;			// Penalty value
    char *stampFile;			// Filename for file with stamps
    bool reverse;			// Reverse the subtraction?
    bool mask;				// Output the mask?
} poisConfig;

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

// A kernel basis function
typedef struct {
    int u, v;				// Offset
    int xOrder, yOrder;			// x^xOrder * y^yOrder
} poisKernelBasis;

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

typedef enum {
    POIS_STAMP_USED,			// Use this stamp
    POIS_STAMP_RESET,			// This stamp has been rejected --- reset
    POIS_STAMP_RECALC,			// Having been reset, this stamp needs to be recalculated
    POIS_STAMP_BAD			// No stamp in this region
} poisStampStatus;

// A stamp
typedef struct {
    int x, y;				// Position
    psImage *matrix;			// Associated matrix
    psVector *vector;			// Associated vector
    poisStampStatus status;		// Status of stamp
} poisStamp;

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

// poisStamp.c

// Construct a stamp
poisStamp *poisStampAlloc(void);

// Destroy a stamp
void poisStampFree(poisStamp *stamp);

// Return true if the stamp is OK
bool poisCheckStamp(const psImage *image, // Image to check for threshold
		    const psImage *mask, // Mask to check for bad pixels
		    int x, int y,	// Pixel coordinates
		    const poisConfig *config // Configuration
    );

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

typedef enum {
    POIS_MASK_OK       = 0x00,		// Fine
    POIS_MASK_BAD      = 0x01,		// Bad pixels
    POIS_MASK_NEAR_BAD = 0x02,		// Near bad pixels
    POIS_MASK_STAMP    = 0x04		// Masking a stamp
} poisMask;

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

// poisParseConfig.c

// Print usage information
void help(void);

// Parse the command line arguments
poisConfig *poisParseConfig(int argc,	// Number of command-line arguments
			    char **argv	// Command-line arguments
    );

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

// poisKernelBasisFunctions.c

// Generate the kernel basis functions for ease of reference in the construction
psArray *poisKernelBasisFunctions(const poisConfig *config // Configuration
    );

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

// poisMakeMask.c

// Generate a mask of bad pixels in the reference and input images --- don't use these pixels for stamps!
psImage *poisMakeMask(psImage *mask,	// Mask to update, or NULL
		      const psImage *refImage, // Reference image
		      const psImage *inImage, // Input image
		      const poisConfig *config // Configuration
		      );

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

// poisFindStamps.c

// Find stamps
psArray *poisFindStamps(psArray *stamps, // Existing list of stamps, or NULL
			const psImage *image, // Image for which to find stamps
			const psImage *mask, // Mask image
			const poisConfig *config	// Configuration values
    );

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

// poisExtractKernel.c

// Create a kernel image from the solution vector
psImage *poisExtractKernel(const psVector *solution, // Vector containing the kernel elements
			   const psArray *kernelParams, // The kernel parameters
			   float x,	// The relative (-1 to 1) x position for which to get the kernel
			   float y,	// The relative (-1 to 1) y position for which to get the kernel
			   const poisConfig *config // Configuration
    );

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

// poisCalculateEquation.c

// Calculate the matrix and vector for the matrix equation
int poisCalculateEquation(psArray *stamps, // The stamps
			  const psImage *refImage, // The reference image
			  const psImage *inImage, // The input image
			  const psArray *kernelParams, // Kernel parameters
			  const poisConfig *config // Configuration
    );

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

// poisConvolveImage.c

// Convolve an image by the kernel
psImage *poisConvolveImage(const psImage *in, // Input image, to be convolved
			   const psImage *mask,	// Mask image
			   const psVector *solution, // The solution vector, containing the coefficients
			   const psArray *kernelParams, // The kernel parameters
			   const poisConfig *config // The configuration
    );

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

// poisCalculateDeviations.c

// Calculate deviations from the best fit for the stamps
psVector *poisCalculateDeviations(psVector *deviations,	// Output array of deviations, or NULL
				  psArray *stamps, // Array of stamps
				  psImage *refImage, // Reference image
				  psImage *inImage, // Input image
				  psImage *mask, // Mask image
				  psArray *kernelParams, // Array of kernel parameters
				  psVector *solution, // Solution vector
				  poisConfig *config // Configuration
    );

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

// poisRejectStamps.c

// Reject stamps, based on the deviations
bool poisRejectStamps(psArray *stamps,	// Array of stamps
		      psImage *mask,	// Mask image
		      const psVector *deviations, // Vector of deviations for the stamps
		      const poisConfig *config // Configuration
    );

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

// poisSolveEquation.c

// Solve the matrix equation
psVector *poisSolveEquation(psVector *solution,	// Solution vector, or NULL
			    const psArray *stamps, // Array of stamps
			    const poisConfig *config
    );

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

// poisAddPenalty.c

// Generate penalty matrix and vector
void poisAddPenalty(psImage *matrix,	// The matrix
		    psVector *vector,	// The vector
		    float penalty,	// Penalty value
		    const psArray *kernels, // Kernel basis functions
		    const poisConfig *config // Configuration
    );

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

// poisReadStamps.c

// Read stamps from a file
psArray *poisReadStamps(psArray *stamps,// Stamps
			FILE **stampFP, // File pointer for stamps file
			const psImage *image, // Image for which to find stamps
			const psImage *mask, // Mask image
			const poisConfig *config // Configuration
    );

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

// poisCheckKernel.c

// Check kernel for power at large radii
bool poisCheckKernel(const psVector *solution,// Kernel solution
		     const psArray *kernels, // Kernel basis functions
		     const poisConfig *config // Configuration
    );

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

#endif
