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

#include "ppImage.h"

bool ppImageRebinChip (pmConfig *config, pmFPAview *view, ppImageOptions *options, char *outName) {

    pmCell *cell;
    pmReadout *inReadout, *outReadout;

    pmFPAfile *outFile = psMetadataLookupPtr (NULL, config->files, outName);
    if (outFile == NULL) return false;

    // XXX double check that chip != -1?

    pmChip *inChip  = pmFPAviewThisChip (view, outFile->src);
    pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
    if (!pmChipCopyStructure (outChip, inChip, outFile->xBin, outFile->yBin)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to copy chip structure.");
        return false;
    }

    while ((cell = pmFPAviewNextCell (view, outFile->src, 1)) != NULL) {
        psLogMsg ("ppImageRebinChip", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
        if (! cell->process || ! cell->file_exists) { continue; }

        // process each of the readouts
        while ((inReadout = pmFPAviewNextReadout (view, outFile->src, 1)) != NULL) {
            if (! inReadout->data_exists) { continue; }

            outReadout = pmFPAviewThisReadout (view, outFile->fpa);

            // run the rebin code
            ppImageRebinReadout (outReadout, inReadout, outFile, options);
        }

        // Get the original values for the binning
        bool mdok = true;               // Status of MD lookup
        int xBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN"); // The binning in x
        if (!mdok || xBin <= 0) {
            psLogMsg(__func__, PS_LOG_WARN, "CELL.XBIN is not set --- assuming 1.\n");
            xBin = 1;
        }
        int yBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN"); // The binning in y
        if (!mdok || yBin <= 0) {
            psLogMsg(__func__, PS_LOG_WARN, "CELL.YBIN is not set --- assuming 1.\n");
            yBin = 1;
        }

        // Update the concepts with the new values for the binning
        pmCell *outCell = pmFPAviewThisCell(view, outFile->fpa); // The output cell
        psMetadataItem *binItem = psMetadataLookup(outCell->concepts, "CELL.XBIN");
        binItem->data.S32 = xBin * outFile->xBin;
        binItem = psMetadataLookup(outCell->concepts, "CELL.YBIN");
        binItem->data.S32 = yBin * outFile->yBin;
    }

    return true;
}

// XXX this should be made consistent with psImageBinning
bool ppImageRebinReadout (pmReadout *output, pmReadout *input, pmFPAfile *outFile, ppImageOptions *options)
{
    PS_ASSERT_PTR_NON_NULL(output, false);
    PS_ASSERT_IMAGE_NON_NULL(output->image, false);
    PS_ASSERT_PTR_NON_NULL(input, false);
    PS_ASSERT_IMAGE_NON_NULL(input->image, false);
    PS_ASSERT_PTR_NON_NULL(outFile, false);

    // the binning process must not change the size of the output image...
    // psStats *stats = psStatsAlloc (PS_STATS_SAMPLE_MEAN);
    // psImageRebin (output->image, input->image, NULL, 0, scale, stats);
    // psFree (stats);

    int dX = outFile->xBin;
    int dY = outFile->yBin;

    int nX = input->image->numCols;
    int nY = input->image->numRows;

    // we should *either* skip masked pixels or replace masked pixels with the specified value 
    

    // do the rebinning by hand, mean only for test
    for (int yOut = 0; yOut < output->image->numRows; yOut++) {
        for (int xOut = 0; xOut < output->image->numCols; xOut++) {
	    float maskedValue = 0.0;
            float value = 0;
            int nPix = 0;
            for (int yIn = yOut * dY; (yIn < yOut * dY + dY) && (yIn < nY); yIn ++) {
                for (int xIn = xOut * dX; (xIn < xOut * dX + dX) && (xIn < nX); xIn ++) {
		    if (input->mask && input->mask->data.U8[yIn][xIn]) {
		      maskedValue = input->image->data.F32[yIn][xIn];
		      continue;
		    }
                    value += input->image->data.F32[yIn][xIn];
                    nPix ++;
                }
            }
	    if (nPix > 0) {
	      output->image->data.F32[yOut][xOut] = value / nPix;
	    } else {
	      if (options->replaceMasked) {
		output->image->data.F32[yOut][xOut] = maskedValue;
	      } else {
		output->image->data.F32[yOut][xOut] = 0.0;
	      }
	    }
        }
    }

    return true;
}
