# 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;
}

pmDetections *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;
    }

    pmDetections *detections = pmDetectionsAlloc();

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

    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;
        peak->SN = snMin + flux;

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

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

    return detections;
}
