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

# include "ppImage.h"

enum {MODE_NONE, MODE_MODEL, MODE_VALUE};

// In this function, we perform the psphot analysis routine for the chip-mosaicked images
bool ppImageReplaceBackground (pmConfig *config, pmFPAview *view, ppImageOptions *options) {

    bool status;
    pmCell *cell;
    pmReadout *readout;

    // find the reference source image
    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PPIMAGE.CHIP");
    if (!status) {
        psError(PSPHOT_ERR_CONFIG, false, "PSPHOT.INPUT I/O file is not defined");
        return false;
    }

    // select the appropriate recipe information
    psMetadata *ppImageRecipe  = psMetadataLookupPtr (&status, config->recipes, "PPIMAGE");

    // select the appropriate recipe information (needed by psphotModelBackground)
    psMetadata *psphotRecipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);

    char *replaceSkyModeWord = psMetadataLookupStr (&status, ppImageRecipe, "REPLACE.MODE");
    float replaceSkyValue = 0.0;

    int replaceSkyMode = MODE_NONE;
    if (!strcasecmp (replaceSkyModeWord, "MODEL")) {
	replaceSkyMode = MODE_MODEL;
    }
    if (!strcasecmp (replaceSkyModeWord, "VALUE")) {
	replaceSkyMode = MODE_VALUE;
	replaceSkyValue = psMetadataLookupF32 (&status, ppImageRecipe, "REPLACE.VALUE");
    }
    psAssert (replaceSkyMode != MODE_NONE, "replace sky mode not defined");

    // XXX Should this be options->maskValue or & ~options->satMask? the latter will leave saturated pixels high
    // psMaskType maskVal  = options->maskValue & ~options->satMask;
    psMaskType maskVal  = options->maskValue;

    pmFPAfile *modelFile = NULL;
    if (replaceSkyMode == MODE_MODEL) {
	// find the model data, if it exists yet (if not, we build it below)
	modelFile = psMetadataLookupPtr(&status, config->files, "PSPHOT.BACKMDL");

	// user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
	psMetadataAddU8 (psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskVal);
    }

    // iterate over the cells and readout for this chip
    while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
	psLogMsg ("ppImagePhotom", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
	if (! cell->process || ! cell->file_exists) { continue; }

	// process each of the readouts
	while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
	    if (! readout->data_exists) { continue; }
	    if (! readout->mask) { continue; }

	    // replace masked pixels with values from model (unbinning not needed)
	    pmReadout *modelReadout = NULL;
	    psImageBinning *binning = NULL;
	    psImage *model = NULL;
	    if (replaceSkyMode == MODE_MODEL) {
		// we are using this pmFPAfile as an I/O file: select readout or create
		if (modelFile) {
		    modelReadout = pmFPAviewThisReadout (view, modelFile->fpa);
		}
		if (!modelReadout) {
		    psphotModelBackground (config, view, "PPIMAGE.CHIP");
		    modelFile = psMetadataLookupPtr(&status, config->files, "PSPHOT.BACKMDL");
		    assert (modelFile);
		    if (modelFile->mode == PM_FPA_MODE_INTERNAL) {
			modelReadout = modelFile->readout;
		    } else {
			modelReadout = pmFPAviewThisReadout (view, modelFile->fpa);
		    }
		    assert (modelReadout);
		}
		binning = psMetadataLookupPtr(&status, psphotRecipe, "PSPHOT.BACKGROUND.BINNING");
		model = modelReadout->image;
	    }

	    psImage *mask = readout->mask;
	    psImage *image = readout->image;

	    for (int iy = 0; iy < image->numRows; iy++) {
		for (int ix = 0; ix < image->numCols; ix++) {
		    if (!(mask->data.U8[iy][ix] && maskVal)) continue;
		    if (replaceSkyMode == MODE_MODEL) {
			image->data.F32[iy][ix] = psImageUnbinPixel_V2(ix, iy, model, binning);
		    } else {
			image->data.F32[iy][ix] = replaceSkyValue;
		    }
		}
	    }
	}
    }

    if (replaceSkyMode == MODE_MODEL) {
	pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
	pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL.STDEV");
    }

    return true;
}
