/** @file  pmSourceIO.c
 *
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-09-22 12:24:17 $
 *
 *  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 "pmFPAview.h"
#include "pmFPAfile.h"

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

// elixir-style FITS table output (header + table in 1st extension)
// this format consists of a header derived from the image header
// followed by a zero-size matrix, followed by the table data
bool pmSourcesWriteCMF (psFits *fits, psArray *sources, psMetadata *header, char *extname)
{

    psArray *table;
    psMetadata *row;
    int i, type;
    psF32 *PAR, *dPAR;
    float lsky;
    bool status;
    psEllipseShape shape;
    psEllipseAxes axes;

    // find config information for output header
    float ZERO_POINT = psMetadataLookupF32 (&status, header, "ZERO_PT");

    table = psArrayAlloc (sources->n);
    table->n = 0;

    for (i = 0; i < sources->n; i++) {
        pmSource *source = (pmSource *) sources->data[i];
        pmModel *model = pmSourceSelectModel (source);
        if (model == NULL)
            continue;

        PAR = model->params->data.F32;
        dPAR = model->dparams->data.F32;

        type = pmSourceDophotType (source);
        lsky = (PAR[PM_PAR_SKY] < 1.0) ? 0.0 : log10(PAR[PM_PAR_SKY]);

        shape.sx  = PAR[PM_PAR_SXX];
        shape.sy  = PAR[PM_PAR_SYY];
        shape.sxy = PAR[PM_PAR_SXY];
        axes = psEllipseShapeToAxes (shape);

        row = psMetadataAlloc ();
        psMetadataAdd (row, PS_LIST_TAIL, "X_PIX",   PS_DATA_F32, "", PAR[PM_PAR_XPOS]);
        psMetadataAdd (row, PS_LIST_TAIL, "Y_PIX",   PS_DATA_F32, "", PAR[PM_PAR_YPOS]);
        psMetadataAdd (row, PS_LIST_TAIL, "MAG_RAW", PS_DATA_F32, "", PS_MIN (99.0, source->psfMag + ZERO_POINT));
        psMetadataAdd (row, PS_LIST_TAIL, "MAG_GAL", PS_DATA_F32, "", PS_MIN (99.0, source->extMag + ZERO_POINT));
        psMetadataAdd (row, PS_LIST_TAIL, "MAG_AP",  PS_DATA_F32, "", PS_MIN (99.0, source->apMag + ZERO_POINT));
        psMetadataAdd (row, PS_LIST_TAIL, "MAG_ERR", PS_DATA_F32, "", PS_MIN (999, 1000*source->errMag));
        psMetadataAdd (row, PS_LIST_TAIL, "LOG_SKY", PS_DATA_F32, "", lsky);
        psMetadataAdd (row, PS_LIST_TAIL, "FWHM_X",  PS_DATA_F32, "", axes.major);
        psMetadataAdd (row, PS_LIST_TAIL, "FWHM_Y",  PS_DATA_F32, "", axes.minor);
        psMetadataAdd (row, PS_LIST_TAIL, "THETA",   PS_DATA_F32, "", axes.theta);
        psMetadataAdd (row, PS_LIST_TAIL, "DOPHOT",  PS_DATA_U8,  "", type);
        psMetadataAdd (row, PS_LIST_TAIL, "WEIGHT",  PS_DATA_U8,  "", PS_MIN (255, PS_MAX(0, 255*source->pixWeight)));

        psArrayAdd (table, 100, row);
        psFree (row);
    }

    // create the basic table header
    psMetadataAdd (header, PS_LIST_HEAD, "EXTNAME", PS_DATA_STRING | PS_META_REPLACE, "extension name", extname);

    // XXX this is broken : does not write the existing header elements except EXTNAME
    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    if (!psFitsWriteTable (fits, header, table, extname)) {
        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
        psFree(table);
        return false;
    }

    psFree (table);
    return true;
}

// read in a readout from the fits file
psArray *pmSourcesReadCMF (psFits *fits, psMetadata *header)
{

    bool status;
    psF32 *PAR, *dPAR;
    psEllipseShape shape;
    psEllipseAxes axes;
    float lsky;

    // define PSF model type
    int modelType = pmModelSetType ("PS_MODEL_GAUSS");

    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
    if (PSF_NAME != NULL) {
        modelType = pmModelSetType (PSF_NAME);
    }

    // find config information for output header
    float ZERO_POINT = psMetadataLookupF32 (&status, header, "ZERO_PT");
    if (!status)
        ZERO_POINT = 25.0;

    psArray *table = psFitsReadTable (fits);
    // validate a single row of the table (must match SMP)

    psArray *sources = psArrayAlloc (table->n);
    sources->n = table->n;

    // convert the table to the pmSource entries
    // XXX need to chooose PSF vs EXT, based on type?
    for (int i = 0; i < table->n; i++) {
        pmSource *source = pmSourceAlloc ();
        pmModel *model = pmModelAlloc (modelType);
        source->modelPSF  = model;

        PAR = model->params->data.F32;
        dPAR = model->dparams->data.F32;

        psMetadata *row = table->data[i];

        lsky             = psMetadataLookupF32 (&status, row, "LOG_SKY");
        PAR[PM_PAR_SKY]  = pow(lsky, 10.0);
        PAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PIX");
        PAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PIX");
        axes.major       = psMetadataLookupF32 (&status, row, "FWHM_X");
        axes.minor       = psMetadataLookupF32 (&status, row, "FWHM_Y");
        axes.theta       = psMetadataLookupF32 (&status, row, "THETA");

        shape = psEllipseAxesToShape (axes);

        PAR[PM_PAR_SXX] = shape.sx;
        PAR[PM_PAR_SYY] = shape.sy;
        PAR[PM_PAR_SXY] = shape.sxy;

        source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
        source->extMag = psMetadataLookupF32 (&status, row, "MAG_GAL") - ZERO_POINT;
        source->errMag = psMetadataLookupF32 (&status, row, "MAG_ERR");
        source->apMag  = psMetadataLookupF32 (&status, row, "MAG_AP")  - ZERO_POINT;

        sources->data[i] = source;
    }
    return (sources);
}
