#ifndef PP_IMAGE_H
#define PP_IMAGE_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <strings.h>  // for strcasecmp
#include <unistd.h>   // for unlink
#include "pslib.h"
#include "psmodules.h"
#include "psphot.h"
#include "psastro.h"
#include "ppStats.h"

#define RECIPE_NAME "PPIMAGE"           // Name of the recipe to use
#define TIMERNAME "ppImage"             // Name of timer

// Options for ppImage processing
typedef struct {
    // actions which ppImage should perform
    bool doMaskBuild;                   // Build internal mask
    bool doWeightBuild;                   // Build internal mask
    bool doMask;                        // Mask bad pixels
    bool doNonLin;                      // Non-linearity correction
    bool doOverscan;                    // Overscan subtraction
    bool doBias;                        // Bias subtraction
    bool doDark;                        // Dark subtraction
    bool doShutter;                     // Shutter correction
    bool doFlat;                        // Flat-field normalisation
    bool doFringe;                      // Fringe subtraction
    bool doPhotom;                      // Source identification and photometry
    bool doAstromChip;                  // per-chip Astrometry
    bool doAstromMosaic;                // full-mosaic Astrometry
    bool doStats;                       // call ppStats on the image
    bool replaceMasked;                 // fill in masked values with background model

    // output files requested
    bool BaseFITS;
    bool BaseMaskFITS;
    bool BaseWeightFITS;

    bool ChipFITS;
    bool ChipMaskFITS;
    bool ChipWeightFITS;

    bool FPA1FITS;
    bool FPA2FITS;
    bool Bin1FITS;
    bool Bin1JPEG;
    bool Bin2FITS;
    bool Bin2JPEG;

    // make values for abstract concepts of masking
    psMaskType maskValue;               // apply this bit-mask to choose masked bits
    psMaskType satMask;                 // Mask value to give saturated pixels
    psMaskType badMask;                 // Mask value to give bad pixels
    psMaskType flatMask;                // Mask value to give bad flat pixels
    psMaskType blankMask;               // Mask value to give blank pixels

    // non-linear correction parameters
    psDataType nonLinearType;
    psMetadataItem *nonLinearData;
    void *nonLinearSource;

    // options for the analysis
    pmOverscanOptions *overscan;        // Overscan options

    // binning parameters
    int xBin1;                          // x-binning, scale 1
    int yBin1;                          // y-binning, scale 1
    int xBin2;                          // x-binning, scale 2
    int yBin2;                          // y-binning, scale 2

    // parameters used by the fringe analysis
    float fringeRej;                    // Fringe rejection limit
    int fringeIter;                     // Fringe iterations
    float fringeKeep;                   // Fringe keep fraction

} ppImageOptions;

// Cells to be used in the detrend
typedef struct {
    pmCell *input;                      // The input cell, to be operated upon
    pmCell *mask;                       // The bad pixel mask
    pmCell *bias;                       // The bias correction
    pmCell *dark;                       // The dark correction
    pmCell *flat;                       // The flat-field correction
} ppImageDetrend;

ppImageOptions *ppImageOptionsAlloc(void);

// Determine the processing options
ppImageOptions *ppImageOptionsParse(pmConfig *config);

// Get the configuration
pmConfig *ppImageArguments(int argc, char **argv);

// Determine what type of camera, and initialise
ppImageOptions *ppImageParseCamera(pmConfig *config);

// Loop over the input
bool ppImageLoop(pmConfig *config, ppImageOptions *options);

// free memory, check for leaks
void ppImageCleanup (pmConfig *config, ppImageOptions *options);

// perform the detrend analysis on the current readout
bool ppImageDetrendReadout (pmConfig *config, ppImageOptions *options, pmFPAview *view);

bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);

bool ppImageDetrendNonLinear(pmReadout *input, ppImageOptions *options);
bool ppImageDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem);
bool ppImageDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem);

bool ppImageDetrendCell(ppImageDetrend *detrend, ppImageOptions *options, pmConfig *config);
bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
pmReadout* ppImageDetrendSelectFirst(pmCell *cell, char *name, bool doThis);

bool ppImageRebinChip (pmConfig *config, pmFPAview *view, char *outName);
bool ppImageRebinReadout (pmReadout *output, pmReadout *input, pmFPAfile *outFile);

bool ppImagePhotom (pmConfig *config, pmFPAview *view);
bool ppImageAstrom (pmConfig *config);
bool ppImageAddstar (pmConfig *config);

bool ppImageReplaceBackground (pmConfig *config, pmFPAview *view, ppImageOptions *options);

bool ppImageMosaicChip (pmConfig *config, const ppImageOptions *options, const pmFPAview *view,
                        const char *outFile, const char *inFile);
bool ppImageMosaicFPA (pmConfig *config, const ppImageOptions *options,
                       const char *outFile, const char *inFile);

void ppImageFileCheck (pmConfig *config);

// functions used by ppFocus
pmConfig *ppFocusArguments(int argc, char **argv);
ppImageOptions *ppFocusParseCamera (pmConfig *config, int entry);
bool ppFocusGetFWHM (pmConfig *config, psVector *focus, psVector *fwhm);
bool ppFocusFitFWHM (pmConfig *config, psVector *focus, psVector *fwhm);

void ppFocusDropCamera (pmConfig *config);

bool ppImageDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType);

// calculate stats, including MD5
bool ppImageStats (pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options);

// write stats to output file
bool ppImageStatsOutput (pmConfig *config, const ppImageOptions *options);


// Measure fringes
bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
                                 pmCell *fringe, // Fringe cell (each readout is a different component)
                                 const bool isResidual,
                                 const ppImageOptions *options // Options
    );

// Solve the fringe system
bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
                               const pmChip *refChip, // Chip with reference fringes
                               const bool isResidual,
                               const ppImageOptions *options // Options
    );

// Generate fringe frame
bool ppImageDetrendFringeGenerate(pmCell *science, // Science cell
                                  pmCell *fringes, // Fringe cell, one readout per fringe component
                                  const ppImageOptions *options // Options
    );


bool ppImageDetrendFringeApply (pmConfig *config, // config
                                pmChip *chip, // science chip
                                const pmFPAview *inputView, // current view
                                const ppImageOptions *options // options
    );

/// Return short version information
psString ppImageVersion(void);

/// Return long version information
psString ppImageVersionLong(void);

/// Update the metadata with version information for all dependencies
void ppImageVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
    );


// calculate stats, including MD5
bool ppImagePixelStats (pmConfig *config, const ppImageOptions *options, const pmFPAview *inputView);

// calculate stats from headers and concepts
bool ppImageMetadataStats (pmConfig *config, const ppImageOptions *options);

void ppImageFileCheck (pmConfig *config);

/// Dump memory summary to text file
void ppImageMemoryDump(const char *description);

#endif
