# include "psphotInternal.h"

// we store the detections on the readout->analysis for each readout this function finds new
// peaks and new footprints.  any old peaks are saved on oldPeaks.  the resulting footprint set
// contains all footprints (old and new)
bool psphotFindDetections (pmConfig *config, const pmFPAview *view, bool firstPass)
{
    bool status = true;

    // select the appropriate recipe information
    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
    psAssert (recipe, "missing recipe?");

    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");

    // loop over the available readouts
    for (int i = 0; i < num; i++) {
	if (!psphotFindDetectionsReadout (config, view, "PSPHOT.INPUT", i, recipe, firstPass)) {
            psError (PSPHOT_ERR_CONFIG, false, "failed to find initial detections for PSPHOT.INPUT entry %d", i);
	    return false;
	}
    }
    return true;
}

// smooth the image, search for peaks, optionally define footprints based on the peaks
bool psphotFindDetectionsReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool firstPass) {

    bool status;
    int pass;
    float NSIGMA_PEAK = 25.0;
    int NMAX = 0;

    // find the currently selected readout
    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filename, index); // File of interest
    psAssert (file, "missing file?");

    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
    psAssert (readout, "missing readout?");

    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
    psAssert (maskVal, "missing mask value?");

    // Use the new pmFootprints approach?
    const bool useFootprints = psMetadataLookupBool(NULL, recipe, "USE_FOOTPRINTS");

    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
    // on the initial pass, detections have not yet been allocated or saved on readout->analysis
    if (!detections) {
	// create the container
        detections = pmDetectionsAlloc();
	
	// save detections on the readout->analysis
	if (!psMetadataAddPtr (readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_META_REPLACE | PS_DATA_UNKNOWN, "psphot detections", detections)) {
	    psError (PSPHOT_ERR_CONFIG, false, "problem saving detections on readout");
	    return false;
	}
    } else {
	psMemIncrRefCounter(detections); // so we can free the detections below
    }

    if (firstPass) {
        pass = 1;
        NSIGMA_PEAK = psMetadataLookupF32 (&status, recipe, "PEAKS_NSIGMA_LIMIT"); PS_ASSERT (status, NULL);
        NMAX = psMetadataLookupS32 (&status, recipe, "PEAKS_NMAX"); PS_ASSERT (status, NULL);
    } else {
        pass = 2;
        NSIGMA_PEAK = psMetadataLookupF32 (&status, recipe, "PEAKS_NSIGMA_LIMIT_2"); PS_ASSERT (status, NULL);
        NMAX = 0; // unlimited number of peaks in final pass: allow a limit (PEAKS_NMAX_2) ?
    }

    float threshold = PS_SQR(NSIGMA_PEAK);

    // move the old peaks array (if it exists) to oldPeaks XXX generically, we should be able
    // to call this function an arbitrary number of times the old peaks are saved so they can
    // be freed later -- the have to be freed after psphotFindFootprints is called below, since
    // they are also owned by the oldFootprints, which are in turn merged into the new
    // footprints.  (what about the source->peak entry?)
    
    assert (detections->oldPeaks == NULL);
    detections->oldPeaks = detections->peaks;
    detections->peaks = NULL;

    // generate the smoothed significance image
    psImage *significance = psphotSignificanceImage (readout, recipe, pass, maskVal);

    // display the significance image
    psphotVisualShowSignificance (significance, -1.0, PS_SQR(3.0*NSIGMA_PEAK));

    // XXX getting some strange results from significance image
    if (0) {
	psImage *lsig = (psImage *) psUnaryOp (NULL, significance, "log");
	psphotVisualShowSignificance (lsig, 0.0, 4.0);
	psFree (lsig);
    }	

    // detect the peaks in the significance image
    detections->peaks = psphotFindPeaks (significance, readout, recipe, threshold, NMAX);
    psMetadataAddF32  (readout->analysis, PS_LIST_TAIL, "PEAK_THRESHOLD", PS_META_REPLACE, "Peak Detection Threshold", threshold);
    if (!detections->peaks) {
	// we only get a NULL peaks array due to a programming or config error. 
	// this will result in a failure.
	psFree (detections);
	psError (PSPHOT_ERR_CONFIG, false, "failed on peak search");
        return false;
    }

    // optionally merge peaks into footprints
    if (useFootprints) {
	psphotFindFootprints (detections, significance, readout, recipe, threshold, pass, maskVal);
    }

    psFree (significance);

    // display the peaks and footprints
    psphotVisualShowPeaks (detections);
    psphotVisualShowFootprints (detections);

    psFree (detections);

    return true;
}

// if we use the footprints, the output peaks list contains both old and new peaks,
// otherwise it only contains the new peaks.
