#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>

#define THREADED 1

#include "pswarpErrorCodes.h"
#define PSWARP_RECIPE  "PSWARP" // Name of the recipe to use
#define PSASTRO_RECIPE "PSASTRO" // Name of the recipe to use

#define PSWARP_ANALYSIS_VARFACTOR "PSWARP.VARFACTOR" // Name for variance factor in analysis metadata
#define PSWARP_ANALYSIS_GOODPIX   "PSWARP.GOODPIX" // Name for number of good pixels in analysis metadata

// a single pswarpMap converts coordinates from one image to a second image
// the linear model is only valid over a limited range of pixels
typedef struct {
    double Xo, Xx, Xy;
    double Yo, Yx, Yy;
    int xo;
    int yo;
} pswarpMap;

// the pswarpMapGrid carries a collection of pswarpMag structures representing the
// local value of the pswarpMap at different locations in the image.
typedef struct {
    pswarpMap ***maps;
    int nXpts, nYpts;                   // number of x,y samples in the grid
    int nXpix, nYpix;                   // x,y spacing in src image pixels of grid samples
    double xMin,  yMin;                 // coordinate of first grid sample
} pswarpMapGrid;

typedef struct {
    // values which are common to all tiles
    pmReadout *input;
    pmReadout *output;
    pswarpMapGrid *grid;
    psImageInterpolateOptions *interp;
    psImage *region;

    // input values for this tile
    int gridX;
    int gridY;

    // output values for this tile
    long goodPixels;                    // Number of good pixels
    int xMin, xMax, yMin, yMax;         // Bounds of tile
} pswarpTransformTileArgs;

pswarpTransformTileArgs *pswarpTransformTileArgsAlloc();
bool pswarpTransformTile (pswarpTransformTileArgs *args);

pmConfig *pswarpArguments (int argc, char **argv);
bool pswarpOptions(pmConfig *config);
bool pswarpSetMaskBits (pmConfig *config);
bool pswarpParseCamera (pmConfig *config);
bool pswarpDefine (pmConfig *config);
bool pswarpLoop (pmConfig *config);
void pswarpCleanup (pmConfig *config);
bool pswarpTransformReadout (pmReadout *output, pmReadout *input, pmConfig *config);
bool pswarpTransformSources(pmReadout *output, pmReadout *input, pmConfig *config);

bool pswarpMatchRange (int *minX, int *minY, int *maxX, int *maxY, pmReadout *dest, pmReadout *src);

pswarpMap *pswarpMapAlloc ();
pswarpMapGrid *pswarpMapGridAlloc (int Nx, int Ny);

pswarpMapGrid *pswarpMapGridFromImage (pmReadout *dest, pmReadout *src, int nXpix, int nYpix);
bool pswarpMapGridSetGrid (pswarpMapGrid *grid, int ix, int iy, int *gridX, int *gridY);
bool pswarpMapGridCoordRange (pswarpMapGrid *grid, int gridX, int gridY, psPlane *min, psPlane *max);
int pswarpMapGridNextGrid_X (pswarpMapGrid *grid, int gridX);
int pswarpMapGridNextGrid_Y (pswarpMapGrid *grid, int gridY);
double pswarpMapGridMaxError (pswarpMapGrid *grid);
bool pswarpMapApply (double *outX, double *outY, pswarpMap *map, double inX, double inY);
bool pswarpMapSetLocalModel (pswarpMap *map, pmReadout *dest, pmReadout *src, int ix, int iy);

bool pswarpDefineSkycell (pmFPAfile **outFile, pmConfig **outConfig, pmConfig *config,
                          const char *filename, const char *argname);

/// Check to see if the readout has a minimum fraction of "lit" pixels
bool pswarpPixelFraction(const pmReadout *readout, ///< Readout to inspect
                         psMetadata *stats, ///< Statistics to update with the result
                         const pmConfig *config ///< Configuration
    );

// define threads for this program
bool pswarpSetThreads ();
