# include "psphotInternal.h"

#define PSF_SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
                         PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
                         PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply for PSF sources

// add newly selected sources to the existing list of sources
bool psphotMergeSources (psArray *oldSources, psArray *newSources) {

    for (int i = 0; i < newSources->n; i++) {
        pmSource *source = newSources->data[i];
        psArrayAdd (oldSources, 100, source);
    }
    return true;
}

// merge the externally supplied sources with the existing sources.  mark them as having
// mode PM_SOURCE_MODE_EXTERNAL
bool psphotLoadExtSources (pmConfig *config, const pmFPAview *view, psArray *sources) {

    // find the currently selected readout
    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT.CMF");
    if (!readout) {
        psLogMsg ("psphot", 3, "no external sources supplied");
        return true;
    }

    psArray *extSources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    if (!extSources) {
        psLogMsg ("psphot", 3, "no external sources for this readout");
        return true;
    }

    for (int i = 0; i < extSources->n; i++) {
        pmSource *source = extSources->data[i];
        source->mode |= PM_SOURCE_MODE_EXTERNAL;
        pmModel *model = source->modelPSF;

        float xpos = model->params->data.F32[PM_PAR_XPOS];
        float ypos = model->params->data.F32[PM_PAR_YPOS];

        source->peak = pmPeakAlloc(xpos, ypos, 1.0, PM_PEAK_LONE);
        source->peak->xf = xpos;
        source->peak->yf = ypos;
        source->peak->flux = 1.0;

        // drop the loaded source modelPSF
        psFree (source->modelPSF);
        source->modelPSF = NULL;
    }

    psphotMergeSources (sources, extSources);
    psLogMsg ("psphot", 3, "%ld external sources merged to yield %ld total sources", extSources->n, sources->n);

    return true;
}

// extract the input sources corresponding to this readout
psArray *psphotLoadPSFSources (pmConfig *config, const pmFPAview *view) {

    // find the currently selected readout
    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT.CMF");
    if (!readout) {
        psLogMsg ("psphot", 3, "readout not found");
        return NULL;
    }

    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    if (!sources) {
        psLogMsg ("psphot", 3, "no psf sources for this readout");
    }

    return sources;
}

// generate the detection structure for the supplied array of sources
pmDetections *psphotDetectionsFromSources (pmConfig *config, psArray *sources) {

    pmDetections *detections = pmDetectionsAlloc();

    detections->peaks = psArrayAllocEmpty(100);

    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
    if (!recipe) {
        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
        return false;
    }
    float snMin = psMetadataLookupF32(NULL, recipe, "MOMENTS_SN_MIN");
    if (!isfinite(snMin)) {
        return NULL;
    }

    for (int i = 0; i < sources->n; i++) {
        pmSource *source = sources->data[i];
        pmModel *model = source->modelPSF;

        if (source->mode & PSF_SOURCE_MASK || !isfinite(source->psfMag)) {
            continue;
        }

        float flux = powf(10.0, -0.4 * source->psfMag);
        float xpos = model->params->data.F32[PM_PAR_XPOS];
        float ypos = model->params->data.F32[PM_PAR_YPOS];

        pmPeak *peak = pmPeakAlloc(xpos, ypos, 1.0, PM_PEAK_LONE);
        peak->xf = xpos;
        peak->yf = ypos;
        peak->flux = flux; // this are being set wrong, but does it matter?

	if (isfinite (source->errMag) && (source->errMag > 0.0)) {
	  peak->SN = 1.0 / source->errMag; 
	} else {
	  peak->SN = 0.0;
	}

        psArrayAdd (detections->peaks, 100, peak);
        psFree (peak);
    }

    psLogMsg ("psphot", 3, "%ld PSF sources loaded", detections->peaks->n);

    return detections;
}

// generate the detection structure for the supplied array of sources
bool psphotSetSourceParams (pmConfig *config, psArray *sources, pmPSF *psf) {

    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
    if (!recipe) {
        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
        return false;
    }

    for (int i = 0; i < sources->n; i++) {
        pmSource *source = sources->data[i];
        pmModel *model = source->modelPSF;

        if (source->mode & PSF_SOURCE_MASK || !isfinite(source->psfMag)) {
            continue;
        }

        float flux = powf(10.0, -0.4 * source->psfMag);
        float xpos = model->params->data.F32[PM_PAR_XPOS];
        float ypos = model->params->data.F32[PM_PAR_YPOS];

        pmPeak *peak = pmPeakAlloc(xpos, ypos, 1.0, PM_PEAK_LONE);
        peak->xf = xpos;
        peak->yf = ypos;
        peak->flux = flux; // this are being set wrong, but does it matter?

	if (isfinite (source->errMag) && (source->errMag > 0.0)) {
	  peak->SN = 1.0 / source->errMag; 
	} else {
	  peak->SN = 0.0;
	}

        source->peak = peak;
    }

    psLogMsg ("psphot", 3, "%ld PSF sources loaded", sources->n);

    return true;
}
