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

#include "pmFPA.h"

psRegion *pmReadoutExtent(const pmReadout *readout)
{
    PS_ASSERT_PTR_NON_NULL(readout, NULL);

    if (readout->image) {
        return psRegionAlloc(readout->col0, readout->image->numCols,
                             readout->row0, readout->image->numRows);
    }
    pmCell *cell = readout->parent;     // The parent cell
    if (!cell) {
        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to determine size of readout.\n");
        return NULL;
    }
    bool mdok;                          // Status of MD lookup
    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
    if (!mdok || !trimsec) {
        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.TRIMSEC.\n");
        return NULL;
    }

    // XXX: Binning?

    return psRegionAlloc(readout->col0, trimsec->x1 - trimsec->x0,
                         readout->row0, trimsec->y1 - trimsec->y0);
}


psRegion *pmCellExtent(const pmCell *cell)
{
    PS_ASSERT_PTR_NON_NULL(cell, NULL);

    psArray *readouts = cell->readouts; // Array of component readouts
    psRegion *cellExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of cell
    for (long i = 0; i < readouts->n; i++) {
        pmReadout *readout = readouts->data[i]; // Readout of interest
        psRegion *roExtent = pmReadoutExtent(readout); // Extent of readout
        cellExtent->x0 = PS_MIN(cellExtent->x0, roExtent->x0);
        cellExtent->x1 = PS_MAX(cellExtent->x1, roExtent->x1);
        cellExtent->y0 = PS_MIN(cellExtent->y0, roExtent->y0);
        cellExtent->y1 = PS_MAX(cellExtent->y1, roExtent->y1);
        psFree(roExtent);
    }

    bool mdok;                          // Status of MD lookup
    int x0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Cell x offset
    if (!mdok) {
        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.X0.\n");
        psFree(cellExtent);
        return NULL;
    }
    int y0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Cell y offset
    if (!mdok) {
        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.Y0.\n");
        psFree(cellExtent);
        return NULL;
    }

    cellExtent->x0 += x0;
    cellExtent->x1 += x0;
    cellExtent->y0 += y0;
    cellExtent->y1 += y0;

    return cellExtent;
}


psRegion *pmChipExtent(const pmChip *chip)
{
    PS_ASSERT_PTR_NON_NULL(chip, NULL);

    psArray *cells = chip->cells;       // Arrya of component cells
    psRegion *chipExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of chip
    for (long i = 0; i < cells->n; i++) {
        pmCell *cell = cells->data[i];  // Cell of interest
        psRegion *cellExtent = pmCellExtent(cell); // Extent of cell
        chipExtent->x0 = PS_MIN(chipExtent->x0, cellExtent->x0);
        chipExtent->x1 = PS_MAX(chipExtent->x1, cellExtent->x1);
        chipExtent->y0 = PS_MIN(chipExtent->y0, cellExtent->y0);
        chipExtent->y1 = PS_MAX(chipExtent->y1, cellExtent->y1);
        psFree(cellExtent);
    }

    bool mdok;                          // Status of MD lookup
    int x0 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0"); // Chip x offset
    if (!mdok) {
        psError(PS_ERR_UNKNOWN, true, "Unable to find CHIP.X0.\n");
        psFree(chipExtent);
        return NULL;
    }
    int y0 = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0"); // Chip y offset
    if (!mdok) {
        psError(PS_ERR_UNKNOWN, true, "Unable to find CHIP.Y0.\n");
        psFree(chipExtent);
        return NULL;
    }

    chipExtent->x0 += x0;
    chipExtent->x1 += x0;
    chipExtent->y0 += y0;
    chipExtent->y1 += y0;

    return chipExtent;
}


psRegion *pmFPAExtent(const pmFPA *fpa)
{
    PS_ASSERT_PTR_NON_NULL(fpa, NULL);

    psArray *chips = fpa->chips;       // Arrya of component chips
    psRegion *fpaExtent = psRegionAlloc(INFINITY, 0, INFINITY, 0); // Extent of fpa
    for (long i = 0; i < chips->n; i++) {
        pmChip *chip = chips->data[i];  // Chip of interest
        psRegion *chipExtent = pmChipExtent(chip); // Extent of chip
        fpaExtent->x0 = PS_MIN(fpaExtent->x0, chipExtent->x0);
        fpaExtent->x1 = PS_MAX(fpaExtent->x1, chipExtent->x1);
        fpaExtent->y0 = PS_MIN(fpaExtent->y0, chipExtent->y0);
        fpaExtent->y1 = PS_MAX(fpaExtent->y1, chipExtent->y1);
        psFree(chipExtent);
    }

    return fpaExtent;
}
