/** @file pswarpLoop.c
 *
 *  @brief main processing loop for pswarp
 *  @ingroup pswarp
 *
 *  @author IfA
 *  @version $Revision: 1.38 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2009-02-13 21:54:32 $
 *  Copyright 2009 Institute for Astronomy, University of Hawaii
 */

#include "pswarp.h"

// Loop over the inputs, warp them to the output skycell and then update metadata
bool pswarpLoop(pmConfig *config, psMetadata *stats)
{

    // load the recipe
    bool status = false;
    psMetadata *recipe = psMetadataLookupPtr (&status, config->recipes, PSWARP_RECIPE);
    if (!recipe) {
        psError(PSWARP_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
        return false;
    }

    // select the input data sources
    pmFPAfile *output = psMetadataLookupPtr(&status, config->files, "PSWARP.OUTPUT");
    if (!output) {
        psError(PSWARP_ERR_CONFIG, true, "Can't find output data!\n");
        return false;
    }

    // use the external astrometry source if supplied
    pmFPAfile *skycell = psMetadataLookupPtr(&status, config->files, "PSWARP.SKYCELL");
    if (!skycell) {
        psError(PSWARP_ERR_DATA, true, "Cannot find output astrometry.");
        return false;
    }

    pmFPAview *view = pmFPAviewAlloc(0);

    int nInputs = psMetadataLookupS32(&status, config->arguments, "NUM_INPUTS");
    if (!status) {
        psError(PSWARP_ERR_DATA, true, "number of inputs is not defined (programming error)");
        return false;
    }

    // load in the input pixel data (ex. background model)
    pmFPAfileActivate(config->files, false, NULL);
    pmFPAfileActivate(config->files, true, "PSWARP.INPUT");
    pmFPAfileActivate(config->files, true, "PSWARP.MASK");
    pmFPAfileActivate(config->files, true, "PSWARP.VARIANCE");

    // We re-activate the CMF load so we can transform the sources as well as the pixels.
    // We only need to read in these if the astrometry source is CMF.
    if (psMetadataLookupBool(&status, recipe, "SOURCES")) {
	pmFPAfileActivate(config->files, true, "PSWARP.ASTROM");
    }

    // loop over this section once per input group
    for (int i = 0; i < nInputs; i++) {
	// select the input data sources
	pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PSWARP.INPUT", i);
	if (!input) {
	    psError(PSWARP_ERR_CONFIG, true, "Can't find input data!\n");
	    return false;
	}

	// select the input data sources
	pmFPAfile *astrom = pmFPAfileSelectSingle(config->files, "PSWARP.ASTROM", i);
	if (!astrom) {
	    astrom = input;
	}

	pmFPAviewReset (view);

	// files associated with the science image
	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
	    psError(psErrorCodeLast(), false, "Unable to read files.");
	    goto FAIL;
	}

	// *** main transformation block 
	// *** this section loops over the input chips/cells and reads them one at a time
	// *** the output chips/cells are filled where appropriate, but not yet written to disk 
	pmChip *chip;
	while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
	    psTrace ("pswarp", 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)) {
		psError(psErrorCodeLast(), false, "Unable to read files.");
		goto FAIL;
	    }

	    pmCell *cell;
	    while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
		psTrace ("pswarp", 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)) {
		    psError(psErrorCodeLast(), false, "Unable to read files.");
		    goto FAIL;
		}

		// process each of the readouts
		pmReadout *readout;
		while ((readout = pmFPAviewNextReadout(view, input->fpa, 1)) != NULL) {
		    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
			psError(psErrorCodeLast(), false, "Unable to read files.");
			goto FAIL;
		    }
		    if (!readout->data_exists) {
			continue;
		    }

		    // Copy the detections from the astrometry carrier to the input, so they can be accessed by
		    // pswarpTransformReadout
		    if (astrom != input) {
			pmReadout *astromRO = pmFPAviewThisReadout(view, astrom->fpa); // Readout for astrometry
			pmDetections *detections = psMetadataLookupPtr(&status, astromRO->analysis, "PSPHOT.DETECTIONS"); // Sources from astrometry
			if (detections) {
			    psMetadataAddPtr(readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_DATA_ARRAY, "Sources from input astrometry", detections);
			}
		    }

		    pswarpTransformToTarget(output->fpa, readout, config, false);
		
		    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
			psError(psErrorCodeLast(), false, "Unable to write files.");
			goto FAIL;
		    }
		}
		if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
		    psError(psErrorCodeLast(), false, "Unable to write files.");
		    goto FAIL;
		}
	    }
	    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
		psError(psErrorCodeLast(), false, "Unable to write files.");
		goto FAIL;
	    }
	}
	if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
	    psError(psErrorCodeLast(), false, "Unable to write files.");
	    goto FAIL;
	}

	if (!pswarpUpdateStatistics (output->fpa, stats, input->fpa, astrom->fpa, config)) {
	    psError(psErrorCodeLast(), false, "problem generating statistics.");
	    goto FAIL;
	}
	if (!pswarpUpdateMetadata (output->fpa, skycell->fpa, input->fpa, astrom->fpa, config, true)) {
	    psError(psErrorCodeLast(), false, "problem generating statistics.");
	    goto FAIL;
	}
    }

    if (!pswarpMakePSF (config, output, stats)) {
      psError(psErrorCodeLast(), false, "problem generating PSF.");
      goto FAIL;
    }

    psFree(view);
    return true;

 FAIL:
    psFree (view);
    return false;
}

// once the output fpa elements have been built, loop over the fpa and generate stats
// for each readout
bool pswarpTransformToTarget (pmFPA *output, pmReadout *input, pmConfig *config, bool backgroundWarp)  {

    pmFPAview *view = pmFPAviewAlloc(0);
    
    pmChip *chip;
    while ((chip = pmFPAviewNextChip (view, output, 1)) != NULL) {
        psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
        if (!chip->process || !chip->file_exists) { continue; }

        pmCell *cell;
        while ((cell = pmFPAviewNextCell (view, output, 1)) != NULL) {
            psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
            if (!cell->process || !cell->file_exists) { continue; }

            // process each of the readouts
            pmReadout *readout;
            while ((readout = pmFPAviewNextReadout(view, output, 1)) != NULL) {
		pswarpTransformReadout (readout, input, config, backgroundWarp);
	    }
	}
    }
    return true;
}

