#ifndef PP_STACK_H
#define PP_STACK_H

#define PPSTACK_RECIPE "PPSTACK"        // Name of the recipe
#define PPSTACK_INSPECT_PIXELS "PPSTACK.PIXELS" // Name of rejected pixels metadata items

#include <pslib.h>
#include <psmodules.h>

// Mask values for inputs
typedef enum {
    PPSTACK_MASK_MATCH  = 0x01,         // PSF-matching failed
    PPSTACK_MASK_REJECT = 0x02,         // Rejection failed
    PPSTACK_MASK_BAD    = 0x04,         // Bad image (too many pixels rejected)
    PPSTACK_MASK_ALL    = 0xff          // All errors
} ppStackMask;

// Thread for stacking chunks
//
// Each input file contributes a readout, into which is read a chunk from that file
typedef struct {
    psArray *readouts;                  // Input readouts to read and stack
    bool read;                          // Has the scan been read?
    bool busy;                          // Is the scan being processed?
    int firstScan;                      // First row of the chunk to be read for this group
    int lastScan;                       // Last row of the chunk to be read for this group
} ppStackThread;

// Allocator
ppStackThread *ppStackThreadAlloc(psArray *readouts // Inputs readouts to read and stack
    );

// Data for threads
typedef struct {
    psArray *threads;                   // Threads doing stacking
    int lastScan;                       // Last row that's been read
    psArray *imageFits;                 // FITS file pointers for images
    psArray *maskFits;                  // FITS file pointers for masks
    psArray *weightFits;                // FITS file pointers for weights
} ppStackThreadData;

// Set up thread data
ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, // Array of input cells
                                          const psArray *imageNames, // Names of images to read
                                          const psArray *maskNames, // Names of masks to read
                                          const psArray *weightNames, // Names of weight maps to read
                                          const pmConfig *config // Configuration
    );

// Read chunk into the first available file thread
ppStackThread *ppStackThreadRead(bool *status, // Status of read
                                 ppStackThreadData *stack, // Stacks available for reading
                                 pmConfig *config, // Configuration
                                 int numChunk, // Chunk number (only for interest)
                                 int overlap // Overlap between subsequent scans
    );

// Initialise the threads
void ppStackThreadInit(void);

// Setup command-line arguments
bool ppStackArgumentsSetup(int argc, char *argv[], // Command-line arguments
                           pmConfig *config  // Configuration
    );

// Parse command-line arguments
bool ppStackArgumentsParse(pmConfig *config  // Configuration
    );

// Parse cameras
bool ppStackCamera(pmConfig *config     // Configuration
    );

// Loop over the inputs, doing the combination
bool ppStackLoop(pmConfig *config       // Configuration
    );

// Determine target PSF for input images
pmPSF *ppStackPSF(const pmConfig *config, // Configuration
                  int numCols, int numRows, // Size of image
                  const psArray *psfs   // List of input PSFs
    );

// Perform stacking on a readout
//
// Returns an array of pixels to inspect for each input image
psArray *ppStackReadoutInitial(const pmConfig *config,   // Configuration
                               pmReadout *outRO,   // Output readout
                               const psArray *readouts, // Input readouts
                               const psArray *regions, // Array with array of regions used in each PSF match
                               const psArray *kernels, // Array with array of kernels used in each PSF match
                               const psVector *addVariance // Additional variance for rejection
    );

// Thread entry point for ppStackReadoutInitial
bool ppStackReadoutInitialThread(psThreadJob *job // Job to process
    );

// Concatenate inspection lists for each input image
bool ppStackInspect(psThreadJob *job    // Job to process
    );

// Perform stacking on a readout
bool ppStackReadoutFinal(const pmConfig *config,   // Configuration
                         pmReadout *outRO,   // Output readout
                         const psArray *readouts, // Input readouts
                         const psArray *rejected // Array with pixels rejected in each image
    );

// Thread entry point for ppStackReadoutFinal
bool ppStackReadoutFinalThread(psThreadJob *job // Job to process
    );

// Perform photometry on stack
bool ppStackPhotometry(pmConfig *config, // Configuration
                       const pmReadout *readout, // Readout to be photometered
                       const pmFPAview *view // View to readout
    );

// Return software version
psString ppStackVersion(void);

// Return long description of software version
psString ppStackVersionLong(void);

// Supplement metadata with software version
void ppStackVersionMetadata(psMetadata *metadata // Metadata to supplement
    );

/// Convolve image to match specified seeing
bool ppStackMatch(pmReadout *readout,   // Readout to be convolved; replaced with output
                  psArray **regions,    // Array of regions used in each PSF matching, returned
                  psArray **kernels,    // Array of kernels used in each PSF matching, returned
                  float *chi2,          // Chi^2 from the stamps
                  const psArray *sources, // Array of sources
                  const pmPSF *psf,     // Target PSF
                  psRandom *rng,        // Random number generator
                  const pmConfig *config // Configuration
    );


/// Add sources to source lists, removing duplicates and solving for magnitude differences
///
/// Corrects the sources to have consistent magnitudes.  Returns the source lists.
psArray *ppStackSourceListAdd(psArray *lists, // List to which to add, or NULL
                              psArray *sources, // Sources to add
                              const pmConfig *config // Configuration
    );

/// Combine source lists to yield a set of unique sources.
///
/// Corrects the sources to have consistent magnitudes where possible.  Returns the sources
psArray *ppStackSourceListCombine(psArray *lists, // Source lists
                                  const pmConfig *config // Configuration
    );

/// Print sources into a ds9 regions file
void ppStackSourcesPrint(const psArray *sources // Sources to print
    );

#endif
