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

#include <stdio.h>
#include <string.h>
#include <pslib.h>
#include <psmodules.h>
#include <ppStats.h>

#include "ppStack.h"

bool ppStackLoop(pmConfig *config)
{
    psMaskType maskBlank = psMetadataLookupU8(NULL, config->arguments, "MASK.BLANK"); // Mask for blank reg.

    bool mdok;                          // Status of MD lookup
    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
    psMetadata *stats = NULL;           // Container for statistics
    FILE *statsFile = NULL;             // File stream for statistics
    if (statsName && strlen(statsName) > 0) {
        psString resolved = pmConfigConvertFilename(statsName, config, true); // Resolved filename
        statsFile = fopen(resolved, "w");
        if (!statsFile) {
            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
            psFree(resolved);
            return false;
        } else {
            stats = psMetadataAlloc();
        }
        psFree(resolved);
    }

    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSTACK.INPUT"); // Token input file
    if (!input) {
        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find input data!\n");
        return false;
    }

    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
    pmHDU *lastHDU = NULL;              // Last HDU that was updated

    // Iterate over the FPA hierarchy
    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
        return false;
    }

    pmChip *chip;                       // Chip of interest
    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
            return false;
        }

        pmCell *cell;                // Cell of interest
        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                return false;
            }

            // Put version information into the header
            pmHDU *hdu = pmHDUFromCell(cell);
            if (hdu && hdu != lastHDU) {
                if (!hdu->header) {
                    hdu->header = psMetadataAlloc();
                }
                ppStackVersionMetadata(hdu->header);
                lastHDU = hdu;
            }

            pmReadout *readout;         // Readout of interest
            while ((readout = pmFPAviewNextReadout(view, input->fpa, 1))) {
                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                    return false;
                }

                // Perform the analysis
                if (!ppStackReadout(config, view)) {
                    psError(PS_ERR_UNKNOWN, false, "Unable to stack images.\n");
                    return false;
                }

                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                    return false;
                }
            }

            // Perform statistics on the cell
            if (stats) {
                ppStatsFPA(stats, input->fpa, view, maskBlank, config);
            }

            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
                return false;
            }
        }

        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
            return false;
        }
    }

    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
        return false;
    }

    psFree(view);

    // Write out summary statistics
    if (stats) {
        const char *statsMDC = psMetadataConfigFormat(stats);
        if (!statsMDC || strlen(statsMDC) == 0) {
            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
        } else {
            fprintf(statsFile, "%s", statsMDC);
        }
        psFree((void *)statsMDC);
        fclose(statsFile);

        psFree(stats);
    }

    return true;
}
