/** @file  pmModelGroup.c
 *
 *  Functions to define and manipulate object model attributes
 *
 *  @author GLG, MHPCC
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.2.4.3 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-04-19 06:31:55 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "pslib.h"
#include "psEllipse.h"
#include "pmHDU.h"
#include "pmFPA.h"
#include "pmPeaks.h"
#include "pmMoments.h"
#include "pmGrowthCurve.h"
#include "pmModel.h"
#include "pmPSF.h"
#include "pmSource.h"
#include "pmModelGroup.h"

// XXX shouldn't these be defined for us in pslib.h ???
double hypot(double x, double y);
double sqrt (double x);

#include "models/pmModel_GAUSS.c"
#include "models/pmModel_PGAUSS.c"
#include "models/pmModel_QGAUSS.c"
#include "models/pmModel_SGAUSS.c"

static pmModelGroup defaultModels[] = {
                                          {"PS_MODEL_GAUSS",        7, pmModelFunc_GAUSS,   pmModelFlux_GAUSS,   pmModelRadius_GAUSS,   pmModelLimits_GAUSS,   pmModelGuess_GAUSS,  pmModelFromPSF_GAUSS,  pmModelFitStatus_GAUSS},
                                          {"PS_MODEL_PGAUSS",       7, pmModelFunc_PGAUSS,  pmModelFlux_PGAUSS,  pmModelRadius_PGAUSS,  pmModelLimits_PGAUSS,  pmModelGuess_PGAUSS, pmModelFromPSF_PGAUSS, pmModelFitStatus_PGAUSS},
                                          {"PS_MODEL_QGAUSS",       8, pmModelFunc_QGAUSS,  pmModelFlux_QGAUSS,  pmModelRadius_QGAUSS,  pmModelLimits_QGAUSS,  pmModelGuess_QGAUSS, pmModelFromPSF_QGAUSS, pmModelFitStatus_QGAUSS},
                                          {"PS_MODEL_SGAUSS",       9, pmModelFunc_SGAUSS,  pmModelFlux_SGAUSS,  pmModelRadius_SGAUSS,  pmModelLimits_SGAUSS,  pmModelGuess_SGAUSS, pmModelFromPSF_SGAUSS, pmModelFitStatus_SGAUSS},
                                      };

static pmModelGroup *models = NULL;
static int Nmodels = 0;

static void ModelGroupFree (pmModelGroup *modelGroup)
{

    if (modelGroup == NULL)
        return;
    return;
}

pmModelGroup *pmModelGroupAlloc (int nModels)
{

    pmModelGroup *modelGroup = (pmModelGroup *) psAlloc (nModels * sizeof(pmModelGroup));
    psMemSetDeallocator(modelGroup, (psFreeFunc) ModelGroupFree);
    return (modelGroup);
}

void pmModelGroupAdd (pmModelGroup *model)
{

    if (models == NULL) {
        pmModelGroupInit ();
    }

    Nmodels ++;
    models = (pmModelGroup *) psRealloc (models, Nmodels*sizeof(pmModelGroup));
    models[Nmodels-1] = model[0];
    return;
}

void pmModelGroupInit (void)
{

    int Nnew = sizeof (defaultModels) / sizeof (pmModelGroup);

    models = pmModelGroupAlloc (Nnew);
    for (int i = 0; i < Nnew; i++) {
        models[i] = defaultModels[i];
    }
    Nmodels = Nnew;
    return;
}

void pmModelGroupCleanup (void)
{

    psFree (models);
    return;
}

pmModelFunc pmModelFunc_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelFunc);
}

pmModelFlux pmModelFlux_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelFlux);
}

pmModelRadius pmModelRadius_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelRadius);
}

pmModelLimits pmModelLimits_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelLimits);
}

pmModelGuessFunc pmModelGuessFunc_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelGuessFunc);
}

pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelFitStatusFunc);
}

pmModelFromPSFFunc pmModelFromPSFFunc_GetFunction (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].modelFromPSFFunc);
}

psS32 pmModelParameterCount (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (0);
    }
    return (models[type].nParams);
}

psS32 pmModelSetType (char *name)
{
    for (int i = 0; i < Nmodels; i++) {
        if (!strcmp(models[i].name, name)) {
            return (i);
        }
    }
    return (-1);
}

char *pmModelGetType (pmModelType type)
{
    if ((type < 0) || (type >= Nmodels)) {
        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
        return (NULL);
    }
    return (models[type].name);
}

/******************************************************************************
    pmSourceModelGuess(source, model): This function allocates a new
    pmModel structure based on the given modelType specified in the argument list.  
    The corresponding pmModelGuess function is returned, and used to 
    supply the values of the params array in the pmModel structure.  
     
    XXX: Many parameters are based on the src->moments structure, which is in
    image, not subImage coords.  Therefore, the calls to the model evaluation
    functions will be in image, not subImage coords.  Remember this.
*****************************************************************************/
pmModel *pmSourceModelGuess(pmSource *source,
                            pmModelType modelType)
{
    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    PS_ASSERT_PTR_NON_NULL(source->moments, false);
    PS_ASSERT_PTR_NON_NULL(source->peak, false);

    pmModel *model = pmModelAlloc(modelType);

    pmModelGuessFunc modelGuessFunc = pmModelGuessFunc_GetFunction(modelType);
    modelGuessFunc(model, source);
    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    return(model);
}

