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

#include "ppImage.h"

// calculate stats, including MD5
bool ppImageStats (pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options) {

    bool mdok;              // Status of MD lookup
    pmCell *cell = NULL;
    pmReadout *readout = NULL;

    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
    *view = *inputView;
    
    // select or create the fpa-level analysis stats metadata:
    psMetadata *stats = psMetadataLookupPtr (&mdok, chip->parent->analysis, "PPIMAGE.STATS");
    psMemIncrRefCounter (stats);
    if (!stats) {
	stats = psMetadataAlloc ();
	if (!psMetadataAdd (chip->parent->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
	    psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
	    psFree (view);
	    psFree (stats);
	    return false;
	}
    }

    view->cell = view->readout = -1;
    while ((cell = pmFPAviewNextCell(view, chip->parent, 1)) != NULL) {
	if (!cell->process || !cell->file_exists) {
	    continue;
	}

	// Perform statistics on the detrended cell
	if (options->doStats) {

	    pmFPAfile *outImage = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
	    pmFPAfile *outPhotom = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.OUTPUT");
	    pmFPAfile *outAstrom = psMetadataLookupPtr(&mdok, config->files, "PSASTRO.OUTPUT");

	    if (!outImage && !outPhotom && !outAstrom) {
		psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find any output file (PPIMAGE.OUTPUT, PSPHOT.OUTPUT, PSASTRO.OUTPUT).");
		psFree (view);
		psFree(stats);
		return false;
	    }

	    // get the latest output product available
	    pmFPAfile *output = outAstrom;
	    if (!output) {
		output = outPhotom;
	    }
	    if (!output) {
		output = outImage;
	    }

	    if (!ppStatsFPA(stats, output->fpa, view,
			 options->satMask | options->badMask | options->maskValue,
			 config)) {
		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
		psFree(view);
		psFree(stats);
		return false;
	    }

	    // extract the fringe amplitude from the analysis
	    if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE", "FRINGE.SOLUTION")) {
		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
		psFree(view);
		psFree(stats);
		return false;
	    }

	    // extract the fringe residual amplitude from the analysis
	    if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE_RESID", "FRINGE.RESIDUAL.SOLUTION")) {
		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
		psFree(view);
		psFree(stats);
		return false;
	    }
	}

	// Add MD5 information for cell
	pmHDU *hdu = pmHDUFromCell(cell); // HDU that owns the cell
	while ((readout = pmFPAviewNextReadout(view, chip->parent, 1)) != NULL) {
	    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
	    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");

	    psString headerName = NULL; // Header name for MD5
	    psStringAppend(&headerName, "MD5_%s_%s_%d", chipName, cellName, view->readout);

	    psVector *md5 = psImageMD5(readout->image); // md5 hash
	    psString md5string = psMD5toString(md5); // String
	    psFree(md5);
	    psMetadataAddStr(hdu->header, PS_LIST_TAIL, headerName, PS_META_REPLACE,
			     "Image MD5", md5string);
	    psFree(md5string);
	    psFree(headerName);
	}
    }
    psFree(view);
    psFree(stats);
    return true;
}
