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

# include "psphot.h"

bool psphotReadout (pmConfig *config, pmFPAview *view) {

    // select the current recipe
    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, "PSPHOT");

    // find the currently selected readout
    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");

    // optional break-point for processing
    char *breakPt = psMetadataLookupStr (NULL, recipe, "BREAK_POINT");

    // XXX does this need to invoke I/O?
    // XXX move this input the psphotImageLoop level?
    if (!pmReadoutGenerateMaskWeight (readout)) {
        return false;
    }

    // I have a valid mask, now mask in the analysis region of interest
    psphotMaskReadout (readout, recipe);

    // run a single-model test if desired
    psphotModelTest (readout, recipe);

    // psphotSaveImage (NULL, readout->image,  "image.fits");
    // psphotSaveImage (NULL, readout->mask,   "mask.fits");
    // psphotSaveImage (NULL, readout->weight, "weight.fits");

    // generate a background model (median, smoothed image)
    psphotImageMedian (config, view);

    // find the peaks in the image
    psArray *peaks = psphotFindPeaks (readout, recipe);

    // construct sources and measure basic stats
    // limit moments analysis by S/N?
    psArray *sources = psphotSourceStats (readout, recipe, peaks);
    psFree (peaks);

    if (!strcasecmp (breakPt, "PEAKS")) {
	psphotReadoutCleanup (config, readout, recipe, NULL, sources);
	return true;
    }

    // mark blended peaks PS_SOURCE_BLEND
    psphotBasicDeblend (sources, recipe);

    // classify sources based on moments, brightness
    // faint sources not classified?
    if (!psphotRoughClass (sources, recipe)) {
        psFree (sources);
        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
        pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
        pmFPAfileDropInternal (config->files, "PSPHOT.BACKGND");
        return false;
    }
    if (!strcasecmp (breakPt, "MOMENTS")) {
	psphotReadoutCleanup (config, readout, recipe, NULL, sources);
	return true;
    }

    // use bright stellar objects to measure PSF
    pmPSF *psf = psphotChoosePSF (readout, sources, recipe);
    if (!strcasecmp (breakPt, "PSFMODEL")) {
	psphotReadoutCleanup (config, readout, recipe, psf, sources);
	return true;
    }

    // linear PSF fit to peaks
    psphotEnsemblePSF (readout, sources, recipe, psf, FALSE);
    if (!strcasecmp (breakPt, "ENSEMBLE")) {
	psphotReadoutCleanup (config, readout, recipe, psf, sources);
	return true;
    }

    // non-linear PSF and EXT fit to brighter sources
    psphotBlendFit (readout, sources, recipe, psf);

    // XXX this was an attempt to measure bias due to the weighting.
    // psphotWeightBias (readout, sources, recipe, psf);

    // replace fitted sources
    psphotReplaceUnfit (sources);

    // XXX find remaining peaks after first source subtraction pass
    // psphotFindPeaks ();

    // linear PSF fit to remaining peaks
    // psphotEnsemblePSF (readout, sources, recipe, psf, TRUE);

    // measure aperture photometry corrections
    if (!psphotApResid (readout, sources, recipe, psf)) {
        psError(PSPHOT_ERR_PHOTOM, false, "Measure aperture photometry corrections");
        return false;
    }

    // calculate source magnitudes
    psphotMagnitudes (sources, recipe, psf);

    // replace background in residual image
    psphotSkyReplace (config, view);

    // drop the references to the image pixels held by each source
    psphotSourceFreePixels (sources);
    // psphotSaveImage (NULL, readout->image, "resid.fits");

    // create the exported-metadata and free local data
    psphotReadoutCleanup (config, readout, recipe, psf, sources);
    return true;
}

bool psphotReadoutCleanup (pmConfig *config, pmReadout *readout, psMetadata *recipe, pmPSF *psf, psArray *sources) {

    // use the psf-model to measure FWHM stats
    if (psf) {
	psphotPSFstats (readout, recipe, psf);
    } else {
	psphotMomentsStats (readout, recipe, sources);
    }

    // create an output header with stats results
    psMetadata *header = psphotDefineHeader (recipe);

    // save the results of the analysis
    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY,    "psphot sources", sources);
    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.HEADER",  PS_DATA_METADATA, "header stats", header);
    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.PSF",     PS_DATA_UNKNOWN,  "psphot psf", psf);

    // remove internal pmFPAfiles, if created
    pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
    pmFPAfileDropInternal (config->files, "PSPHOT.BACKGND");

    psFree (psf);
    psFree (header);
    psFree (sources);

    return true;
}
