/** @file  pmModelGroup.c
 *
 *  Functions to define and manipulate object model attributes
 *
 *  @author GLG, MHPCC
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2007-07-20 00:31:43 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

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

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <pslib.h>
#include "pmHDU.h"
#include "pmFPA.h"
#include "pmPeaks.h"
#include "pmMoments.h"
#include "pmGrowthCurve.h"
#include "pmResiduals.h"
#include "pmModel.h"
#include "pmResiduals.h"
#include "pmPSF.h"
#include "pmSource.h"
#include "pmModelGroup.h"
#include "pmErrorCodes.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_RGAUSS.c"
# include "models/pmModel_SERSIC.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_RGAUSS",       8, pmModelFunc_RGAUSS,  pmModelFlux_RGAUSS,  pmModelRadius_RGAUSS,  pmModelLimits_RGAUSS,  pmModelGuess_RGAUSS, pmModelFromPSF_RGAUSS, pmModelFitStatus_RGAUSS},
                                          {"PS_MODEL_SERSIC",       8, pmModelFunc_SERSIC,  pmModelFlux_SERSIC,  pmModelRadius_SERSIC,  pmModelLimits_SERSIC,  pmModelGuess_SERSIC, pmModelFromPSF_SERSIC, pmModelFitStatus_SERSIC}
                                      };

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;
}

bool pmModelGroupInit (void)
{

    // if we do not need to init, return false;
    if (models != NULL)
        return false;

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

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

void pmModelGroupCleanup (void)
{
    psFree (models);
    models = NULL;
    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("psModules.objects", 3, "---- %s() begin ----\n", __func__);
    PS_ASSERT_PTR_NON_NULL(source, NULL);
    PS_ASSERT_PTR_NON_NULL(source->moments, NULL);
    PS_ASSERT_PTR_NON_NULL(source->peak, NULL);

    pmModel *model = pmModelAlloc(modelType);

    pmModelGuessFunc modelGuessFunc = pmModelGuessFunc_GetFunction(modelType);
    if (!modelGuessFunc(model, source)) {
	psFree (model);
	return NULL;
    }

    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
    return(model);
}

