/* @file  pmModel.h
 * @brief Functions to define and manipulate object models
 *
 * @author GLG, MHPCC
 * @author EAM, IfA
 *
 * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
 * @date $Date: 2009-02-16 22:30:50 $
 *
 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

# ifndef PM_MODEL_H
# define PM_MODEL_H

#include <pslib.h>
#include "pmPSF.h"

/// @addtogroup Objects Object Detection / Analysis Functions
/// @{

/* pointers for the functions types below are supplied to each pmModel, and can be used by
   the programmer without needing to know the model class */

typedef enum {
    PM_MODEL_STATUS_NONE         = 0x00, ///< model fit not yet attempted, no other info
    PM_MODEL_STATUS_FITTED       = 0x01, ///< model fit completed
    PM_MODEL_STATUS_NONCONVERGE  = 0x02, ///< model fit did not converge
    PM_MODEL_STATUS_OFFIMAGE     = 0x04, ///< model fit drove out of range
    PM_MODEL_STATUS_BADARGS      = 0x08, ///< model fit called with invalid args
    PM_MODEL_STATUS_LIMITS       = 0x10  ///< model parameters hit limits
} pmModelStatus;

typedef enum {
    PM_MODEL_OP_NONE    = 0x00,
    PM_MODEL_OP_FUNC    = 0x01,
    PM_MODEL_OP_RES0    = 0x02,
    PM_MODEL_OP_RES1    = 0x04,
    PM_MODEL_OP_FULL    = 0x07,
    PM_MODEL_OP_SKY     = 0x08,
    PM_MODEL_OP_CENTER  = 0x10,
    PM_MODEL_OP_NORM    = 0x20,
    PM_MODEL_OP_NOISE   = 0x40,
} pmModelOpMode;

/// Parameter limit types
typedef enum {
    PM_MODEL_LIMITS_NONE,               ///< Apply no limits: suitable for debugging
    PM_MODEL_LIMITS_IGNORE,             ///< Ignore all limits: fit can go to town
    PM_MODEL_LIMITS_LAX,                ///< Lax limits: attempting to reproduce mildly bad data
    PM_MODEL_LIMITS_STRICT,             ///< Strict limits: good quality data
} pmModelLimitsType;

typedef struct pmModel pmModel;
typedef struct pmSource pmSource;

//  This function is the model chi-square minimization function for this model.
typedef psMinimizeLMChi2Func pmModelFunc;

//  This function sets the parameter limits for this model.
typedef psMinimizeLMLimitFunc pmModelLimits;

// This function returns the integrated flux for the given model parameters.
typedef psF64 (*pmModelFlux)(const psVector *params);

// This function returns the radius at which the given model and parameters
// achieves the given flux.
typedef psF64 (*pmModelRadius)(const psVector *params, double flux);

//  This function provides the model guess parameters based on the details of
//  the given source.
typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);

//  This function constructs the PSF model for the given source based on the
//  supplied psf and the EXT model for the object.
typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelEXT, const pmPSF *psf);

//  This function sets the model parameters based on the PSF for a given coordinate and central
//  intensity
typedef bool (*pmModelParamsFromPSF)(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);

//  This function returns the success / failure status of the given model fit
typedef bool (*pmModelFitStatusFunc)(pmModel *model);

//  This function sets the parameter limits for the given model
typedef bool (*pmModelSetLimitsFunc)(pmModelLimitsType type);

/** pmModel data structure
 *
 * Every source may have two types of models: a PSF model and a EXT (extended-source)
 * model. The PSF model represents the best fit of the image PSF to the specific
 * object. In this case, the PSF-dependent parameters are specified for the
 * object by the PSF, not by the fit. The EXT model represents the best fit of
 * the given model to the object, with all shape parameters floating in the fit.
 *
 */
struct pmModel {
    pmModelType type;                   ///< Model to be used.
    psVector *params;                   ///< Paramater values.
    psVector *dparams;                  ///< Parameter errors.
    float chisq;                        ///< Fit chi-squared.
    float chisqNorm;                    ///< re-normalized fit chi-squared.
    float mag;                          ///< integrated model magnitude
    float magErr;                       ///< integrated model magnitude error
    int nPix;                           ///< number of pixels used for fit
    int nDOF;                           ///< number of degrees of freedom
    int nIter;                          ///< number of iterations to reach min
    pmModelStatus flags;                ///< model status flags
    float fitRadius;                    ///< fit radius actually used
    pmResiduals *residuals;             ///< normalized PSF residuals

    // functions for this model which depend on the model class
    pmModelFunc          modelFunc;
    pmModelFlux          modelFlux;
    pmModelRadius        modelRadius;
    pmModelLimits        modelLimits;
    pmModelGuessFunc     modelGuess;
    pmModelFromPSFFunc   modelFromPSF;
    pmModelParamsFromPSF modelParamsFromPSF;
    pmModelFitStatusFunc modelFitStatus;
    pmModelSetLimitsFunc modelSetLimits;
};

/** Symbolic names for the elements of [d]params
 * Note: these are #defines not enums as a given element of [d]params
 * may/will correspond to different parameters in different contexts
 */
#define PM_PAR_SKY 0   ///< Sky
#define PM_PAR_I0 1   ///< Central intensity
#define PM_PAR_XPOS 2   ///< X centre of object
#define PM_PAR_YPOS 3   ///< Y centre of object
#define PM_PAR_SXX 4   ///< shape X^2 moment
#define PM_PAR_SYY 5   ///< shape Y^2 moment
#define PM_PAR_SXY 6   ///< shape XY moment
#define PM_PAR_7 7   ///< ??? Unknown parameter
#define PM_PAR_8 8   ///< ??? Unknown parameter

/** pmModelAlloc()
 *
 */
pmModel *pmModelAlloc(pmModelType type);
bool psMemCheckModel(psPtr ptr);

// copy model to a new structure
pmModel *pmModelCopy (pmModel *model);

psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row);
psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, psS32 col, psS32 row, int dx, int dy);

/** pmModelAdd()
 *
 * Add the given source model flux to/from the provided image. The boolean
 * option center selects if the source is re-centered to the image center or if
 * it is placed at its centroid location. The boolean option sky selects if the
 * background sky is applied (TRUE) or not. The pixel range in the target image
 * is at most the pixel range specified by the source.pixels image. The success
 * status is returned.
 *
 */
bool pmModelAdd(
    psImage *image,                     ///< The output image (float)
    psImage *mask,                      ///< The image pixel mask (valid == 0)
    pmModel *model,                     ///< The input pmModel
    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
    psImageMaskType maskVal             ///< Value to mask
);

/** pmModelSub()
 *
 * Subtract the given source model flux to/from the provided image. The
 * boolean option center selects if the source is re-centered to the image center
 * or if it is placed at its centroid location. The boolean option sky selects if
 * the background sky is applied (TRUE) or not. The pixel range in the target
 * image is at most the pixel range specified by the source.pixels image. The
 * success status is returned.
 *
 */
bool pmModelSub(
    psImage *image,                     ///< The output image (float)
    psImage *mask,                      ///< The image pixel mask (valid == 0)
    pmModel *model,                     ///< The input pmModel
    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
    psImageMaskType maskVal             ///< Value to mask
);

bool pmModelAddWithOffset(psImage *image,
                          psImage *mask,
                          pmModel *model,
                          pmModelOpMode mode,
                          psImageMaskType maskVal,
                          int dx,
                          int dy);

bool pmModelSubWithOffset(psImage *image,
                          psImage *mask,
                          pmModel *model,
                          pmModelOpMode mode,
                          psImageMaskType maskVal,
                          int dx,
                          int dy);

/** pmModelFitStatus()
 *
 * This function wraps the call to the model-specific function returned by
 * pmModelFitStatusFunc_GetFunction.  The model-specific function examines the
 * model parameters, parameter errors, Chisq, S/N, and other parameters available
 * from model to decide if the particular fit was successful or not.
 *
 * XXX: Must code this.
 *
 */
bool pmModelFitStatus(
    pmModel *model                      ///< Model to be used
);


/// Set the model parameter limits for the given model
///
/// Wraps the model-specific pmModelSetLimitsFunc function.
bool pmModelSetLimits(
    const pmModel *model,               ///< Model of interest
    pmModelLimits type                  ///< Type of limits
    );


/// @}
# endif /* PM_MODEL_H */
