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

#include <stdio.h>
#include <pslib.h>

#include "pmFPA.h"
#include "pmFPABin.h"

bool pmReadoutRebin(pmReadout *out, const pmReadout *in, psMaskType maskVal, int xBin, int yBin)
{
    PM_ASSERT_READOUT_NON_NULL(out, false);
    PM_ASSERT_READOUT_NON_NULL(in, false);

    psImage *inImage = in->image, *inMask = in->mask; // Input image
    int numColsIn = inImage->numCols, numRowsIn = inImage->numRows; // Size of input image

    psImageBinning *binning = psImageBinningAlloc(); // Binning instructions
    binning->nXbin = xBin;
    binning->nYbin = yBin;
    binning->nXfine = numColsIn;
    binning->nYfine = numRowsIn;
    binning->nXskip = 0;
    binning->nYskip = 0;
    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);

    int numColsOut = binning->nXruff, numRowsOut = binning->nYruff; // Size of output image

    // Output image
    psImage *outImage = out->image = psImageRecycle(out->image,  numColsOut, numRowsOut, PS_TYPE_F32);

    int xLast = numColsIn - 1, yLast = numRowsIn - 1; // Last index
    int yStart = psImageBinningGetFineY(binning, 0); // Starting input y for binning
    for (int yOut = 0; yOut < numRowsOut; yOut++) {
        int yStop = psImageBinningGetFineY(binning, yOut + 1); // Stopping input y for binning
        yStop = PS_MIN(yStop, yLast);
        int xStart = psImageBinningGetFineX(binning, 0); // Starting input x for binning
        for (int xOut = 0; xOut < numColsOut; xOut++) {
            int xStop = psImageBinningGetFineX(binning, xOut + 1); // Stopping input x for binning
            xStop = PS_MIN(xStop, xLast);

            float sum = 0.0;            // Sum of pixels
            int numPix = 0;             // Number of pixels
            for (int y = yStart; y < yStop; y++) {
                for (int x = xStart; x < xStop; x++) {
                    if (inMask && (inMask->data.PS_TYPE_MASK_DATA[y][x] & maskVal)) {
                        continue;
                    }
                    sum += inImage->data.F32[y][x];
                    numPix++;
                }
            }

            outImage->data.F32[yOut][xOut] = numPix > 0 ? sum / numPix : NAN;
            xStart = xStop;
        }
        yStart = yStop;
    }

    psFree(binning);

    out->data_exists = true;
    if (out->parent) {
        pmCell *outCell = out->parent;  // Output cell
        outCell->data_exists = outCell->parent->data_exists = true;

        if (in->parent) {
            pmCell *inCell = in->parent;// Input cell
            psAssert(outCell != inCell, "Can't copy in place!");
            psFree(outCell->concepts);
            outCell->concepts = psMetadataCopy(NULL, inCell->concepts);
        }

        // Update the concepts with the new values for the binning
        psMetadataItem *binItem = psMetadataLookup(outCell->concepts, "CELL.XBIN");
        binItem->data.S32 *= xBin;
        binItem = psMetadataLookup(outCell->concepts, "CELL.YBIN");
        binItem->data.S32 *= yBin;
    }

    return true;
}
