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

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

#include "ppMerge.h"
#include "ppMergeCheckInputs.h"
#include "ppMergeData.h"

// Check input files to make sure everything's consistent
ppMergeData *ppMergeCheckInputs(ppMergeOptions *options, // Options
                                pmConfig *config // Configuration
    )
{
    ppMergeData *data = ppMergeDataAlloc(); // The data, to return

    // Output file
    psString outName = psMetadataLookupStr(NULL, config->arguments, "OUTPUT"); // The output file name
    assert(outName);                    // It should be there!
    outName = pmConfigConvertFilename(outName, config, true);
    data->outFile = psFitsOpen(outName, "w"); // Output FITS file
    if (!data->outFile) {
        // There's no point in continuing if we can't open the output
        psErrorStackPrint(stderr, "Can't open output image: %s\n", outName);
        psFree(outName);
        exit(EXIT_FAILURE);
    }
    psFree(outName);

    // Statistics file
    psString statsName = psMetadataLookupStr(NULL, config->arguments, "-stats"); // Name for statistics file
    if (statsName && strlen(statsName) > 0) {
        psString resolved = pmConfigConvertFilename(statsName, config, true); // Resolved filename
        data->statsFile = fopen(resolved, "w");
        if (!data->statsFile) {
            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
            psFree(resolved);
            psFree(data);
            return NULL;
        }
        psFree(resolved);
    }

    psArray *filenames = psMetadataLookupPtr(NULL, config->arguments, "INPUT"); // The input file names
    assert(filenames);
    if (!data->in) {
        data->in = psArrayAlloc(filenames->n);
    }
    if (!data->files) {
        data->files = psArrayAlloc(filenames->n);
    }
    int numGood = 0;                    // Number of good files
    for (int i = 0; i < filenames->n; i++) {
        psString name = filenames->data[i]; // The name of the file
        if (!name || strlen(name) == 0) {
            continue;
        }
        psTrace("ppMerge", 1, "Checking input file %s....\n", name);
        psString resolved = pmConfigConvertFilename(name, config, false); // Resolved file name
        psFits *inFile = psFitsOpen(resolved, "r"); // The FITS file to read
        if (!inFile) {
            psLogMsg(__func__, PS_LOG_WARN, "Unable to open input file %s --- ignored.\n", resolved);
            // Kick it out
            psFree(filenames->data[i]);
            filenames->data[i] = NULL;
            continue;
        }
        psFree(resolved);
        psMetadata *header = psFitsReadHeader(NULL, inFile); // The FITS (primary) header
        data->files->data[i] = inFile;

        // The formats must be identical.  The chief reason for this is so that we know what output format to
        // use.  I guess one could specify a different output format on the command line, but how do we
        // generate a PHU for that?  Perhaps we could revisit this restriction in the future (construct an
        // FPAview from the specified camera format configuration, and use pmFPAAddSourceFromView), but for
        // now it's less hassle just to limit the output format to be the input format.
        if (!options->format) {
            options->format = pmConfigCameraFormatFromHeader(config, header, true);
            psFree(header);
            if (!options->format) {
                psLogMsg(__func__, PS_LOG_WARN, "Unable to identify camera format for input file %s --- "
                             "ignored.\n", name);
                // Kick it out
                psFree(header);
                data->in->data[i] = NULL;
                continue;
            }
        } else {
          bool valid = false;
          if (!pmConfigValidateCameraFormat(&valid, options->format, header)) {
            psError (PS_ERR_UNKNOWN, false, "Error in config scripts\n");
            exit (PS_EXIT_CONFIG_ERROR);
          }
          if (!valid) {
            psLogMsg(__func__, PS_LOG_WARN, "Input file %s doesn't match camera format --- ignored.\n", name);
            // Kick it out
            psFree(header);
            data->in->data[i] = NULL;
            continue;
          }
        }

        data->in->data[i] = pmFPAConstruct(config->camera);
        pmFPAview *view = pmFPAAddSourceFromHeader(data->in->data[i], header, options->format);
        psFree(view);

        // Use the first valid input as the basis for the output --- including the header
        if (!data->out) {
            psTrace("ppMerge", 5, "Constructing output using %s as a template.\n", name);
            data->out = pmFPAConstruct(config->camera);
            pmFPAview *view = pmFPAAddSourceFromHeader(data->out, header, options->format);
            psFree(view);
        }
        psFree(header);

        psTrace("ppMerge", 3, "%s checks out.\n", name);
        numGood++;
    }

    // Count the cells
    int numCells = 0;           // Number of cells in the output FPA
    psArray *chips = data->out->chips; // Array of chips in output
    for (int i = 0; i < chips->n; i++) {
        pmChip *chip = chips->data[i];  // Chip of interest
        if (!chip) {
            continue;
        }
        psArray *cells = chip->cells;   // Array of cells
        for (int j = 0; j < cells->n; j++) {
            pmCell *cell = cells->data[j];
                if (cell) {
                    numCells++;
                }
        }
    }
    data->numCells = numCells;
    psTrace("ppMerge", 3, "Output has %d cells.\n", numCells);

    psTrace("ppMerge", 3, "We have %d good inputs.\n", numGood);
    if (numGood > 1) {
        return data;
    }

    psFree(data);
    return NULL;
}


