/** @file  pmModelGroup.c
 *
 *  Functions to define and manipulate object model attributes
 *
 *  @author GLG, MHPCC
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2008-12-08 02:51:14 $
 *
 *  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 "pmSpan.h"
#include "pmFootprint.h"
#include "pmPeaks.h"
#include "pmMoments.h"
#include "pmGrowthCurve.h"
#include "pmResiduals.h"
#include "pmModel.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);
}

bool psMemCheckModelGroup(psPtr ptr)
{
    PS_ASSERT_PTR(ptr, false);
    return ( psMemGetDeallocator(ptr) == (psFreeFunc) ModelGroupFree);
}

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