# include "psphotInternal.h"

# define CHECK_STATUS(S,MSG) {						\
	if (!status) {							\
	    psError(PSPHOT_ERR_CONFIG, false, "missing PSF Clump entry: %s\n", MSG); \
	    return false;						\
	} }

bool psphotRoughClassRegion (int nRegion, psRegion *region, psArray *sources, psMetadata *recipe, const bool havePSF);

// 2006.02.02 : no leaks
bool psphotRoughClass (pmReadout *readout, psArray *sources, psMetadata *recipe, const bool havePSF) {

    bool status;

    psTimerStart ("psphot.rough");

    // we make this measurement on a NxM grid of regions across the readout
    int NX  = psMetadataLookupS32 (&status, recipe, "PSF_CLUMP_NX");  CHECK_STATUS (status, "PSF_CLUMP_NX");
    int NY  = psMetadataLookupS32 (&status, recipe, "PSF_CLUMP_NY");  CHECK_STATUS (status, "PSF_CLUMP_NY");
    int dX  = readout->image->numCols / NX;
    int dY  = readout->image->numRows / NY;

    int nRegion = 0;
    for (int ix = 0; ix < NX; ix ++) {
	for (int iy = 0; iy < NY; iy ++) {

	    psRegion *region = psRegionAlloc (ix*dX, (ix + 1)*dX, iy*dY, (iy + 1)*dY);
	    if (!psphotRoughClassRegion (nRegion, region, sources, recipe, havePSF)) {
		psLogMsg ("psphot", 4, "Failed to determine rough classification for region %f,%f - %f,%f\n", 
			 region->x0, region->y0, region->x1, region->y1);
		psFree (region);
		continue;
	    }
	    psFree (region);
	    nRegion ++;
	}
    }
    psMetadataAddS32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegion);

    // optional printout of source moments only
    psphotDumpMoments (recipe, sources);

    psLogMsg ("psphot.roughclass", PS_LOG_INFO, "rough classification: %f sec\n", psTimerMark ("psphot.rough"));

    psphotVisualPlotMoments (recipe, sources);
    psphotVisualShowRoughClass (sources);
    // XXX better visualization: psphotVisualShowFlags (sources);

    return true;
}

bool psphotRoughClassRegion (int nRegion, psRegion *region, psArray *sources, psMetadata *recipe, const bool havePSF) {

    bool status;
    char regionName[64];
    pmPSFClump psfClump;

    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
    psImageMaskType maskSat = psMetadataLookupImageMask(&status, recipe, "MASK.SAT"); // Mask value for bad pixels
    assert (maskSat);

    snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", nRegion);
    psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
    if (!regionMD) {
	// allocate the region metadata folder and add this region to it.
	regionMD = psMetadataAlloc();
	psMetadataAddMetadata (recipe, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
	psFree (regionMD);
    }
    psMetadataAddPtr (regionMD, PS_LIST_TAIL, "REGION", PS_DATA_REGION | PS_META_REPLACE, "psf clump region", region);

    if (!havePSF) {
	// determine the PSF parameters from the source moment values
	// XXX why not save the psfClump as a PTR?
	psfClump = pmSourcePSFClump (region, sources, recipe);
	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
    } else {
	// pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
	psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X"); 
	if (!status) {
	    psLogMsg ("psphot", 4, "No PSF clump defined for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
	    return false;
	}	    
	psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   psAssert (status, "missing PSF.CLUMP.Y");
	psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  psAssert (status, "missing PSF.CLUMP.DX");
	psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  psAssert (status, "missing PSF.CLUMP.DY");
    }

    if (psfClump.X < 0) {
	psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourcePSFClump");
	return false;
    }
    if (!psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {
	psLogMsg ("psphot", 4, "Failed to find a valid PSF clump for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
	return false;
    }
    psLogMsg ("psphot", 3, "psf clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
    psLogMsg ("psphot", 3, "psf clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);

    // group into STAR, COSMIC, EXTENDED, SATURATED, etc.
    if (!pmSourceRoughClass (region, sources, recipe, psfClump, maskSat)) {
	psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourceRoughClass");
	return false;
    }

    return true;
}
