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

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

#include "ppStack.h"
#include "ppStackLoop.h"

#define TIMER_NAME "PPSTACK"            // Name of timer

int main(int argc, char *argv[])
{
    psLibInit(NULL);
    psTimerStart(TIMER_NAME);
    psTimerStart("PPSTACK_STEPS");

    pmErrorRegister();
    ppStackErrorRegister();
    psphotErrorRegister();

    pmConfig *config = pmConfigRead(&argc, argv, PPSTACK_RECIPE); // Configuration
    ppStackOptions *options = NULL;                               // Options for stacking
    if (!config) {
        goto die;
    }

    ppStackVersionPrint();

    if (!pmModelClassInit()) {
        psError(PPSTACK_ERR_PROG, false, "Unable to initialise model classes.");
        goto die;
    }

    if (!psphotInit()) {
        psError(PPSTACK_ERR_PROG, false, "Error initialising psphot.");
        goto die;
    }

    (void)psTraceSetLevel("ppStack", 5);

    if (!ppStackArgumentsSetup(argc, argv, config)) {
        goto die;
    }

    if (!ppStackCamera(config)) {
        goto die;
    }

    if (!ppStackArgumentsParse(config)) {
        goto die;
    }

    options = ppStackOptionsAlloc();
    if (!ppStackLoop(config, options)) {
        goto die;
    }


 die:
    // Common code for the death.
    {
        psExit exitValue = ppStackExitCode(PS_EXIT_SUCCESS); // Exit code

        // Ensure everything closes
        if (config) {
            ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
            ppStackFileActivation(config, PPSTACK_FILES_CONVOLVE, true);
            ppStackFileActivation(config, PPSTACK_FILES_STACK, true);
            ppStackFileActivation(config, PPSTACK_FILES_UNCONV, true);
            ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
            if (!ppStackFilesIterateUp(config)) {
                psError(psErrorCodeLast(), false, "Unable to close files.");
                exitValue = ppStackExitCode(exitValue);
                pmFPAfileFreeSetStrict(false);
            }
        }

        // Write out summary statistics
        if (options && options->stats) {

            psMetadataAddS32(options->stats, PS_LIST_TAIL, "QUALITY", PS_META_REPLACE,
                             "Bad data quality flag", options->quality);
            psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_STACK", 0,
                             "Total time", psTimerClear("PPSTACK_TOTAL"));

            const char *statsMDC = psMetadataConfigFormat(options->stats);
            if (!statsMDC || strlen(statsMDC) == 0) {
                psError(PS_ERR_IO, false, "Unable to get statistics MDC file.");
                return false;
            }
            if (fprintf(options->statsFile, "%s", statsMDC) != strlen(statsMDC)) {
                psError(PS_ERR_IO, false, "Unable to write statistics MDC file.");
                return false;
            }
            psFree(statsMDC);
            if (fclose(options->statsFile) == EOF) {
                psError(PS_ERR_IO, false, "Unable to close statistics MDC file.");
                return false;
            }
            options->statsFile = NULL;
            pmConfigRunFilenameAddWrite(config, "STATS",
                                        psMetadataLookupStr(NULL, config->arguments, "STATS"));
        }
        psFree(options);

        // Dump configuration
        bool mdok;                                                                    // Status of MD lookup
        psString dump = psMetadataLookupStr(&mdok, config->arguments, "DUMP_CONFIG"); // File for config
        if (dump && !pmConfigDump(config, dump)) {
            psError(psErrorCodeLast(), false, "Unable to dump configuration.");
            exitValue = ppStackExitCode(exitValue);
        }

        psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark(TIMER_NAME));
        psTimerStop();

        psFree(config);
        pmModelClassCleanup();
        pmConfigDone();
        psLibFinalize();
        pmVisualClose();

        exitValue = ppStackExitCode(exitValue);
        exit(exitValue);
    }
}

