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


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

#include "ppSim.h"


// The idea is that the pattern should be completely *deterministic* (despite the use of 'random' numbers ---
// with the seed specified, they should be deterministic) so that multiple calls of this function will result
// in the same pattern.  These are set to (really) random values so that they do not dark-subtract or
// flat-field.

bool ppSimBadPixels(pmReadout *readout, const pmConfig *config, psRandom *rng)
{
    PS_ASSERT_PTR_NON_NULL(readout, false);
    PS_ASSERT_IMAGE_NON_NULL(readout->image, false);
    PS_ASSERT_PTR_NON_NULL(readout->parent, false);
    PS_ASSERT_PTR_NON_NULL(config, false);

    bool mdok;                          // Status of MD lookup
    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
    if (!recipe) {
        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSIM_RECIPE);
        return false;
    }

    psU64 seed = psMetadataLookupU64(&mdok, recipe, "BADPIX.SEED"); // Seed for RNG
    if (seed == 0 || !mdok) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "BADPIX.SEED not set, or zero.");
        return false;
    }

    float frac = psMetadataLookupF32(&mdok, recipe, "BADPIX.FRAC"); // Fraction of bad pixels
    if (!mdok) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "BADPIX.FRAC not set.");
        return false;
    }
    if (frac == 0.0) {
        // Nothing to do
        return true;
    }

    psRandom *pseudoRNG = psRandomAlloc(PS_RANDOM_TAUS, seed); // Pseudo-random number generator

    psImage *image = readout->image;    // Image of interest
    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 (psRandomUniform(pseudoRNG) < frac) {
                image->data.F32[y][x] *= psRandomGaussian(rng);
            }
        }
    }

    psFree(pseudoRNG);

    return true;
}
