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

#include "ppImage.h"

bool ppImageLoop (pmConfig *config, ppImageOptions *options) {

    bool status;
    pmChip *chip;
    pmCell *cell;
    pmReadout *readout;

    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
    if (!status) {
        psErrorStackPrint(stderr, "Can't find input data!\n");
        ppImageCleanup(config, options);
        exit(PS_EXIT_PROG_ERROR);
    }

    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
    pmHDU *lastHDU = NULL;              // Last HDU that was updated

    // files associated with the science image
    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
        psFree (view);
        return false;
    }

    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
        if (!chip->process || !chip->file_exists) {
            continue;
        }

        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
            psFree (view);
            return false;
        }

        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
            psLogMsg ("ppImageLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
            if (!cell->process || !cell->file_exists) {
                continue;
            }
            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                psFree (view);
                return false;
            }

            // Put version information into the header
            pmHDU *hdu = pmHDUFromCell(cell);
            if (hdu && hdu != lastHDU) {
                ppImageVersionMetadata(hdu->header);
                lastHDU = hdu;
            }


            // process each of the readouts
            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                    psFree (view);
                    return false;
                }
                if (!readout->data_exists) {
                    continue;
                }

                // perform the detrend analysis
                if (!ppImageDetrendReadout(config, options, view)) {
                    psError(PS_ERR_UNKNOWN, false, "Unable to detrend readout.\n");
                    psFree (view);
                    return false;
                }

            }
        }

        // Apply the fringe correction
        if (options->doFringe) {
	    if (!ppImageDetrendFringeApply (config, chip, view, options)) {
                psFree (view);
                return false;
            }
	}
	
	// measure various statistics for this image
	if (!ppImagePixelStats (config, options, view)) {
	    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to measures stats for image");
	    psFree (view);
	    return false;
	}

        if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT")) {
            psError(PS_ERR_UNKNOWN, false, "Unable to mosaic chip.\n");
            psFree (view);
            return false;
        }
        if (!ppImageRebinChip(config, view, "PPIMAGE.BIN1")) {
            psError(PS_ERR_UNKNOWN, false, "Unable to bin chip.\n");
            psFree (view);
            return false;
        }
        if (!ppImageRebinChip(config, view, "PPIMAGE.BIN2")) {
            psError(PS_ERR_UNKNOWN, false, "Unable to bin chip.\n");
            psFree (view);
            return false;
        }

        // we perform photometry on the readouts of this chip in the output
        if (options->doPhotom) {
            if (!ppImagePhotom(config, view)) {
                psError(PS_ERR_UNKNOWN, false, "error running photometry.\n");
                psFree (view);
                return false;
            }
        }

        // Close cells (XXX shouldn't pmFPAfileClose iterate down as needed?)
        view->cell = -1;
        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
            if (!cell->process || !cell->file_exists) {
                continue;
            }

            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
                psFree(view);
                return false;
            }
        }

        // Close chip
        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
            psFree(view);
            return false;
        }
    }

    // XXX an optional full-scale FPA mosaic could happen here
    // it would require us to NOT free PPIMAGE.CHIP until here
    // ppImageMosaicFPA (config, "PPIMAGE.OUTPUT.FPA", "PPIMAGE.CHIP");
    ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA1", "PPIMAGE.BIN1");
    ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA2", "PPIMAGE.BIN2");

    // we perform astrometry on all chips after sources have been detected
    if (options->doAstromChip || options->doAstromMosaic) {
        if (!ppImageAstrom(config)) {
            psError(PS_ERR_UNKNOWN, false, "error running astrometry.\n");
            psFree(view);
            return false;
        }
    }

    if (psTraceGetLevel("ppImage") >= 3) {
	ppImageFileCheck (config);
    }

    // Write out summary statistics
    if (!ppImageMetadataStats (config, options)) {
	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
	psFree (view);
	return false;
    }


    // Write out summary statistics
    if (!ppImageStatsOutput (config, options)) {
	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
	psFree (view);
	return false;
    }

    // Output and Close FPA
    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
        psFree(view);
        return false;
    }

    psFree(view);
    return true;
}
