/** @file  pmPSF_IO.c
 *
 * This file contains functions to read and write PSF models using the psMetadata Config file
 * format.
 *
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2007-01-26 00:05:18 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

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

/*****************************************************************************/
/* INCLUDE FILES                                                             */
/*****************************************************************************/

#include <pslib.h>
#include "pmHDU.h"
#include "pmFPA.h"
#include "pmFPAfile.h"
#include "pmFPAfile.h"

#include "pmPeaks.h"
#include "pmMoments.h"
#include "pmGrowthCurve.h"
#include "pmModel.h"
#include "pmPSF.h"
#include "pmPSF_IO.h"
#include "pmSource.h"
#include "pmModelGroup.h"
#include "pmSourceIO.h"

psMetadata *pmPSFtoMetadata (psMetadata *metadata, pmPSF *psf)
{

    if (metadata == NULL) {
        metadata = psMetadataAlloc ();
    }

    char *modelName = pmModelGetType (psf->type);
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_MODEL_NAME", PS_DATA_STRING, "PSF model name", modelName);

    int nPar = pmModelParameterCount (psf->type)    ;
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_MODEL_NPAR", PS_DATA_S32, "PSF model parameter count", nPar);

    for (int i = 0; i < nPar; i++) {
        psPolynomial2D *poly = psf->params_NEW->data[i];
        if (poly == NULL)
            continue;
        psPolynomial2DtoMetadata (metadata, poly, "PSF_PAR%02d", i);
    }
    psPolynomial4DtoMetadata (metadata, psf->ApTrend, "APTREND");

    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_AP_RESID", PS_DATA_F32, "aperture residual", psf->ApResid);
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_dAP_RESID", PS_DATA_F32, "aperture residual scatter", psf->dApResid);
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_SKY_BIAS", PS_DATA_F32, "sky bias level", psf->skyBias);

    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_CHISQ", PS_DATA_F32, "chi-square for fit", psf->chisq);
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_NSTARS", PS_DATA_S32, "number of stars used to measure PSF", psf->nPSFstars);
    psMetadataAdd (metadata, PS_LIST_TAIL, "PSF_POISSON_ERRORS", PS_DATA_BOOL, "Poisson errors for fits", psf->poissonErrors);

    return metadata;
}

pmPSF *pmPSFfromMetadata (psMetadata *metadata)
{

    bool status;
    char keyword[80];

    char *modelName = psMetadataLookupPtr (&status, metadata, "PSF_MODEL_NAME");
    pmModelType type = pmModelSetType (modelName);

    bool poissonErrors = psMetadataLookupPtr (&status, metadata, "PSF_POISSON_ERRORS");
    if (!status)
        poissonErrors = true;

    // we determine the PSF parameter polynomials from the MD-defined polynomials
    pmPSF *psf = pmPSFAlloc (type, poissonErrors, NULL);

    int nPar = psMetadataLookupS32 (&status, metadata, "PSF_MODEL_NPAR");
    if (nPar != pmModelParameterCount (psf->type))
        psAbort ("read PSF" , "mismatch model par count");

    // un-fitted terms, not in the Metadata, are left NULL
    // XXX add a double-check of the expected number?
    for (int i = 0; i < nPar; i++) {
        sprintf (keyword, "PSF_PAR%02d", i);
        psMetadata *folder = psMetadataLookupPtr (&status, metadata, keyword);
        if (!status)
            continue;
        psPolynomial2D *poly = psPolynomial2DfromMetadata (folder);
        psFree (psf->params_NEW->data[i]);
        psf->params_NEW->data[i] = poly;
    }
    sprintf (keyword, "APTREND");
    psMetadata *folder = psMetadataLookupPtr (&status, metadata, keyword);
    psPolynomial4D *poly = psPolynomial4DfromMetadata (folder);
    psFree (psf->ApTrend);
    psf->ApTrend = poly;

    psf->ApResid = psMetadataLookupF32 (&status, metadata, "PSF_AP_RESID");
    psf->dApResid = psMetadataLookupF32 (&status, metadata, "PSF_dAP_RESID");
    psf->skyBias = psMetadataLookupF32 (&status, metadata, "PSF_SKY_BIAS");

    psf->chisq = psMetadataLookupF32 (&status, metadata, "PSF_CHISQ");
    psf->nPSFstars = psMetadataLookupS32 (&status, metadata, "PSF_NSTARS");

    psFree (metadata);
    return (psf);
}

bool pmFPAviewWritePSFmodel (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    pmFPA *fpa = file->fpa;

    if (view->chip == -1) {
        pmFPAWritePSFmodel (fpa, view, file, config);
        return true;
    }

    if (view->chip >= fpa->chips->n) {
        return false;
    }
    pmChip *chip = fpa->chips->data[view->chip];

    if (view->cell == -1) {
        pmChipWritePSFmodel (chip, view, file, config);
        return true;
    }

    if (view->cell >= chip->cells->n) {
        return false;
    }
    pmCell *cell = chip->cells->data[view->cell];

    if (view->readout == -1) {
        pmCellWritePSFmodel (cell, view, file, config);
        return true;
    }

    if (view->readout >= cell->readouts->n) {
        return false;
    }
    pmReadout *readout = cell->readouts->data[view->readout];

    pmReadoutWritePSFmodel (readout, view, file, config);
    return true;
}

// read in all chip-level PSFmodel files for this FPA
bool pmFPAWritePSFmodel (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < fpa->chips->n; i++) {

        pmChip *chip = fpa->chips->data[i];
        pmChipWritePSFmodel (chip, view, file, config);
    }
    return true;
}

// read in all cell-level PSFmodel files for this chip
bool pmChipWritePSFmodel (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < chip->cells->n; i++) {

        pmCell *cell = chip->cells->data[i];
        pmCellWritePSFmodel (cell, view, file, config);
    }
    return true;
}

// read in all readout-level PSFmodel files for this cell
bool pmCellWritePSFmodel (pmCell *cell, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < cell->readouts->n; i++) {

        pmReadout *readout = cell->readouts->data[i];
        pmReadoutWritePSFmodel (readout, view, file, config);
    }
    return true;
}

// read in all readout-level Objects files for this cell
bool pmReadoutWritePSFmodel (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{
    bool status;
    char *filename;
    char *realname;

    pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");

    switch (file->type) {
    case PM_FPA_FILE_PSF:
        filename = pmFPAfileNameFromRule (file->filerule, file, view);
        bool create = file->mode == PM_FPA_MODE_WRITE ? true : false;
        realname = pmConfigConvertFilename (filename, config, create);

        psMetadata *psfData = pmPSFtoMetadata (NULL, psf);
        psMetadataConfigWrite (psfData, realname);
        psFree (psfData);
        psFree (realname);
        psFree (filename);
        return true;

    default:
        fprintf (stderr, "warning: type mismatch\n");
        break;
    }
    return false;
}



bool pmFPAviewReadPSFmodel (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    pmFPA *fpa = file->fpa;

    if (view->chip == -1) {
        pmFPAReadPSFmodel (fpa, view, file, config);
        return true;
    }

    if (view->chip >= fpa->chips->n) {
        return false;
    }
    pmChip *chip = fpa->chips->data[view->chip];

    if (view->cell == -1) {
        pmChipReadPSFmodel (chip, view, file, config);
        return true;
    }

    if (view->cell >= chip->cells->n) {
        return false;
    }
    pmCell *cell = chip->cells->data[view->cell];

    if (view->readout == -1) {
        pmCellReadPSFmodel (cell, view, file, config);
        return true;
    }

    if (view->readout >= cell->readouts->n) {
        return false;
    }
    pmReadout *readout = cell->readouts->data[view->readout];

    pmReadoutReadPSFmodel (readout, view, file, config);
    return true;
}

// read in all chip-level PSFmodel files for this FPA
bool pmFPAReadPSFmodel (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < fpa->chips->n; i++) {

        pmChip *chip = fpa->chips->data[i];
        pmChipReadPSFmodel (chip, view, file, config);
    }
    return true;
}

// read in all cell-level PSFmodel files for this chip
bool pmChipReadPSFmodel (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < chip->cells->n; i++) {

        pmCell *cell = chip->cells->data[i];
        pmCellReadPSFmodel (cell, view, file, config);
    }
    return true;
}

// read in all readout-level PSFmodel files for this cell
bool pmCellReadPSFmodel (pmCell *cell, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    for (int i = 0; i < cell->readouts->n; i++) {

        pmReadout *readout = cell->readouts->data[i];
        pmReadoutReadPSFmodel (readout, view, file, config);
    }
    return true;
}

// read in all readout-level Objects files for this cell
bool pmReadoutReadPSFmodel (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, pmConfig *config)
{

    unsigned int Nfail;
    char *filename;
    char *realname;

    switch (file->type) {
    case PM_FPA_FILE_PSF:
        filename = pmFPAfileNameFromRule (file->filerule, file, view);
        bool create = file->mode == PM_FPA_MODE_WRITE ? true : false;
        realname = pmConfigConvertFilename (filename, config, create);

        psMetadata *psfData = psMetadataConfigRead(NULL, &Nfail, realname, FALSE);
        pmPSF *psf = pmPSFfromMetadata (psfData);
        psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN, "psphot psf", psf);

        psFree (psf);
        psFree (psfData);
        psFree (realname);
        psFree (filename);

        return true;

    default:
        fprintf (stderr, "warning: type mismatch\n");
        break;
    }
    return false;
}

