#include <stdio.h>
#include "pmFPA.h"
#include "pmHDU.h"
#include "pmHDUUtils.h"


pmHDU *pmHDUFromFPA(const pmFPA *fpa    // FPA for which to find HDU
                   )
{
    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
    return fpa->hdu;
}

pmHDU *pmHDUFromChip(const pmChip *chip // Chip for which to find HDU
                    )
{
    PS_ASSERT_PTR_NON_NULL(chip, NULL);

    pmHDU *hdu = chip->hdu;             // The HDU information
    if (!hdu) {
        hdu = pmHDUFromFPA(chip->parent); // Grab HDU info from the FPA
    }

    return hdu;
}

pmHDU *pmHDUFromCell(const pmCell *cell // Cell for which to find HDU
                    )
{
    PS_ASSERT_PTR_NON_NULL(cell, NULL);

    pmHDU *hdu = cell->hdu;             // The HDU information
    if (!hdu) {
        hdu = pmHDUFromChip(cell->parent); // Grab HDU info from the chip
    }

    return hdu;
}

pmHDU *pmHDUFromReadout(const pmReadout *readout // Readout for which to find HDU
                       )
{
    PS_ASSERT_PTR_NON_NULL(readout, NULL);

    pmCell *cell = readout->parent; // cell containing this readout;
    pmHDU *hdu = pmHDUFromCell(cell);
    return hdu;
}

// Get the lowest HDU
pmHDU *pmHDUGetLowest(const pmFPA *fpa, // The FPA
                      const pmChip *chip, // The chip, or NULL
                      const pmCell *cell // The cell, or NULL
                     )
{
    pmHDU *hdu = NULL;          // The HDU that's at the lowest level
    if (cell) {
        hdu = pmHDUFromCell(cell);
    } else if (chip) {
        hdu = pmHDUFromChip(chip);
    } else if (fpa) {
        hdu = pmHDUFromFPA(fpa);
    }

    return hdu;
}

// Get the highest HDU
pmHDU *pmHDUGetHighest(const pmFPA *fpa, // The FPA
                       const pmChip *chip, // The chip, or NULL
                       const pmCell *cell // The cell, or NULL
                      )
{
    pmHDU *hdu = NULL;          // The HDU that's at the highest level
    if (fpa) {
        hdu = pmHDUFromFPA(fpa);
    }
    if (!hdu && chip) {
        hdu = pmHDUFromChip(chip);
    }
    if (!hdu && cell) {
        hdu = pmHDUFromCell(cell);
    }

    return hdu;
}

void pmHDUPrint(FILE *fd,               // File descriptor to which to print
                const pmHDU *hdu,       // HDU to print
                int level,              // Level at which to print
                bool header             // Print header?
               )
{
    PS_ASSERT_PTR_NON_NULL(hdu,);

    if (hdu->blankPHU) {
        psTrace(__func__, level, "HDU: (PHU)\n");
    } else {
        psTrace(__func__, level, "HDU: %s\n", hdu->extname);
    }

    psTrace(__func__, level + 1, "Format: %x\n", hdu->format);
    if (header) {
        if (hdu->header) {
            psTrace(__func__, level + 1, "Header:\n");
            psMetadataPrint(fd, hdu->header, level + 2);
        } else {
            psTrace(__func__, level + 1, "No header.\n");
        }
    }

    if (hdu->images) {
        psTrace(__func__, level + 1, "Images:\n");
        for (long i = 0; i < hdu->images->n; i++) {
            psImage *image = hdu->images->data[i]; // Image of interest
            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, image->numCols, image->numRows);
        }
    } else {
        psTrace(__func__, level + 1, "NO images.\n");
    }

    if (hdu->masks) {
        psTrace(__func__, level + 1, "Masks:\n");
        for (long i = 0; i < hdu->masks->n; i++) {
            psImage *mask = hdu->masks->data[i]; // Mask of interest
            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, mask->numCols, mask->numRows);
        }
    } else {
        psTrace(__func__, level + 1, "NO masks.\n");
    }


    if (hdu->weights) {
        psTrace(__func__, level + 1, "Weights:\n");
        for (long i = 0; i < hdu->masks->n; i++) {
            psImage *weight = hdu->weights->data[i]; // Weight image of interest
            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, weight->numCols, weight->numRows);
        }
    } else {
        psTrace(__func__, level + 1, "NO weights.\n");
    }


    return;
}
