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

#include <stdio.h>
#include <pslib.h>
#include <psmodules.h>
#include <psphot.h>
#include "ppSub.h"

// Copied from ppImageSubtractBackground()
bool ppSubBackground(pmReadout *ro, pmConfig *config, const pmFPAview *view)
{
    psAssert(config, "Need configuration");
    psAssert(view, "Need view to chip");

    // The background model file may be defined, though the model hasn't been built
    // If so, we will build it below.
    bool status;                        // Status of lookup
    pmFPAfile *modelFile = psMetadataLookupPtr(&status, config->files, "PSPHOT.BACKMDL"); // Background model
    if (!status) {
        psError(PS_ERR_UNEXPECTED_NULL, true, "PSPHOT.BACKMDL file is not defined");
        return false;
    }

    psMetadata *ppSubRecipe = psMetadataLookupPtr(NULL, config->recipes, PPSUB_RECIPE);
    psAssert(ppSubRecipe, "Need PPSUB recipe");
    psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE);
    psAssert(psphotRecipe, "Need PSPHOT recipe");

    psString maskBadStr = psMetadataLookupStr(NULL, ppSubRecipe, "MASK.BAD"); // Name of bits to mask for bad
    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels

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

    psImage *image = ro->image, *mask = ro->mask; // Image and mask of interest

    pmReadout *modelRO = pmFPAviewThisReadout(view, modelFile->fpa); // Background model
    if (!modelRO) {
        // Create the background model
        if (!psphotModelBackground(config, view, "PPSUB.OUTPUT")) {
            psError(PS_ERR_UNKNOWN, false, "Unable to model background");
            return false;
        }
        modelRO = (modelFile->mode == PM_FPA_MODE_INTERNAL) ? modelFile->readout :
                   pmFPAviewThisReadout(view, modelFile->fpa);
        if (!modelRO) {
            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find background model");
            return false;
        }
    }
    psImageBinning *binning = psMetadataLookupPtr(&status, psphotRecipe,
                                                  "PSPHOT.BACKGROUND.BINNING"); // Binning for model
    psImage *modelImage = modelRO->image; // Background model

    // Do the background subtraction
    int numCols = image->numCols, numRows = image->numRows; // Size of image
    for (int y = 0; y < numRows; y++) {
        for (int x = 0; x < numCols; x++) {
            if (mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskBad) {
                image->data.F32[y][x] = 0.0;
            } else {
                float value = psImageUnbinPixel(x + 0.5, y + 0.5, modelImage, binning); // Background value
                if (!isfinite(value)) {
                    image->data.F32[y][x] = NAN;
                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskBad;
                } else {
                    image->data.F32[y][x] -= value;
                }
            }
        }
    }

    pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL");
    pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL.STDEV");

    return true;
}
