/** @file ppSubLoop.c
 *
 *  @brief
 *
 *  @ingroup ppSub
 *
 *  @author IfA
 *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2009-02-18 00:31:20 $
 *  Copyright 2009 Institute for Astronomy, University of Hawaii
 */

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

#include <pslib.h>
#include <psmodules.h>

#include "ppSub.h"

bool ppSubLoop(ppSubData *data)
{
    psAssert(data, "Require processing data");
    pmConfig *config = data->config;    // Configuration
    psAssert(config, "Require configuration.");

    pmConfigCamerasCull(config, NULL);
    pmConfigRecipesCull(config, "PPSUB,PPSTATS,PSPHOT,PSASTRO,MASKS,JPEG");


    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT");
    pmFPAfile *reference = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF");
    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSUB.OUTPUT");
    psAssert(input && reference && output, "Require files");

    if (!ppSubFilesIterateDown(config, PPSUB_FILES_INPUT | PPSUB_FILES_CONV)) {
        psError(PPSUB_ERR_IO, false, "Unable to load files.");
        return false;
    }

    psTimerStart("PPSUB_MATCH");

    if (!ppSubSetMasks(config)) {
        psError(psErrorCodeLast(), false, "Unable to set masks.");
        return false;
    }

    if (!ppSubMatchPSFs(data)) {
        psError(psErrorCodeLast(), false, "Unable to match PSFs.");
        return false;
    }
    if (data->quality) {
        // Can't do anything at all
        return true;
    }
    // generate the residual stamp grid for visualization
    if (!ppSubResidualSampleJpeg(config)) {
        psError(psErrorCodeLast(), false, "Unable to update.");
        return false;
    }

    psMetadataAddF32(data->stats, PS_LIST_TAIL, "TIME_MATCH", 0, "Time to match PSFs",
                     psTimerClear("PPSUB_MATCH"));

    // Close input files
    if (!ppSubFilesIterateUp(config, PPSUB_FILES_INPUT)) {
        psError(PPSUB_ERR_IO, false, "Unable to close input files.");
        return false;
    }

    if (!ppSubLowThreshold(data)) {
        psError(psErrorCodeLast(), false, "Unable to threshold images.");
        return false;
    }

    // Set up subtraction files
    if (!ppSubFilesIterateDown(config, PPSUB_FILES_SUB)) {
        psError(PPSUB_ERR_IO, false, "Unable to set up subtraction files.");
        return false;
    }

    if (!ppSubDefineOutput("PPSUB.OUTPUT", config)) {
        psError(psErrorCodeLast(), false, "Unable to define output.");
        return false;
    }

    if (!data->quality && !ppSubMakePSF(data)) {
        psError(psErrorCodeLast(), false, "Unable to generate PSF.");
        return false;
    }

    // Now we've got a PSF, blow away detections in case they're confused with real output detections
    {
        pmFPAview *view = ppSubViewReadout(); // View to readout
        pmReadout *out = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
        if (psMetadataLookup(out->analysis, "PSPHOT.DETECTIONS")) {
            psMetadataRemoveKey(out->analysis, "PSPHOT.DETECTIONS");
        }
        psFree(view);
    }

    if (!ppSubReadoutSubtract(config)) {
        psError(psErrorCodeLast(), false, "Unable to subtract images.");
        return false;
    }

    // Close convolved files
    if (!ppSubFilesIterateUp(config, PPSUB_FILES_PSF | PPSUB_FILES_CONV)) {
        psError(PPSUB_ERR_IO, false, "Unable to close input files.");
        return false;
    }

    // Higher order background subtraction using psphot
    if (!ppSubBackground(config)) {
        psError(psErrorCodeLast(), false, "Unable to subtract background.");
        return false;
    }

    // Perform Variance correction (rescale within a modest range)
    if (!ppSubVarianceRescale(config)) {
        psError(psErrorCodeLast(), false, "Unable to rescale variance.");
        return false;
    }

    if (!ppSubFilesIterateDown(config, PPSUB_FILES_PHOT_SUB)) {
        psError(PPSUB_ERR_IO, false, "Unable to set up photometry files.");
        return false;
    }

    if (!data->quality && !ppSubReadoutPhotometry("PPSUB.OUTPUT", data)) {
        psError(psErrorCodeLast(), false, "Unable to perform photometry.");
        return false;
    }

    if (!ppSubFilesIterateUp(config, PPSUB_FILES_PHOT_SUB)) {
        psError(PPSUB_ERR_IO, false, "Unable to set up photometry files.");
        return false;
    }

    // Perform statistics on the cell
    if (!ppSubReadoutStats(data)) {
        psError(psErrorCodeLast(), false, "Unable to collect statistics");
        return false;
    }

    // generate the binned image used to write the jpeg
    if (!ppSubReadoutJpeg(config)) {
        psError(psErrorCodeLast(), false, "Unable to update.");
        return false;
    }

    if (data->inverse) {
        // Set up inverse subtraction files
        if (!ppSubFilesIterateDown(config, PPSUB_FILES_INV)) {
            psError(PPSUB_ERR_IO, false, "Unable to set up inverse files.");
            return false;
        }

        if (data->inverse && !ppSubDefineOutput("PPSUB.INVERSE", config)) {
            psError(psErrorCodeLast(), false, "Unable to define inverse.");
            return false;
        }

        if (!ppSubReadoutInverse(config)) {
            psError(psErrorCodeLast(), false, "Unable to invert images.");
            return false;
        }

        // Close subtraction files and open inverse photometry files
        if (!ppSubFilesIterateUp(config, PPSUB_FILES_SUB)) {
            psError(PPSUB_ERR_IO, false, "Unable to close subtraction files.");
            return false;
        }

        if (!ppSubFilesIterateDown(config, PPSUB_FILES_PHOT_INV)) {
            psError(PPSUB_ERR_IO, false, "Unable to set up inverse files.");
            return false;
        }

        if (!data->quality && !ppSubReadoutPhotometry("PPSUB.INVERSE", data)) {
            psError(psErrorCodeLast(), false, "Unable to perform photometry.");
            return false;
        }

        // Close inverse subtraction files
        if (!ppSubFilesIterateUp(config, PPSUB_FILES_INV | PPSUB_FILES_PHOT_INV)) {
            psError(PPSUB_ERR_IO, false, "Unable to close subtraction files.");
            return false;
        }
    } else {
        // Close subtraction files
        if (!ppSubFilesIterateUp(config, PPSUB_FILES_SUB)) {
            psError(PPSUB_ERR_IO, false, "Unable to close subtraction files.");
            return false;
        }
    }

    return true;
}
