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

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

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

bool ppStackLoop(pmConfig *config)
{
    assert(config);

    psTimerStart("PPSTACK_TOTAL");
    psTimerStart("PPSTACK_STEPS");

    ppStackOptions *options = ppStackOptionsAlloc(); // Options for stacking

    // Setup
    psTrace("ppStack", 1, "Setup....\n");
    if (!ppStackSetup(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to setup.");
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0: Setup: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("setup");


    // Preparation for stacking
    psTrace("ppStack", 1, "Preparation for stacking: merging sources, determining target PSF....\n");
    if (!ppStackPrepare(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to prepare for stacking.");
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1: Load Sources and Generate Target PSF: %f sec",
             psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("prepare");


    // Convolve inputs
    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
    if (!ppStackConvolve(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to convolve images.");
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
             psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("convolve");


    // Start threading
    ppStackThreadInit();
    ppStackThreadData *stack = ppStackThreadDataSetup(options, config);
    if (!stack) {
        psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
        psFree(options);
        return false;
    }
    psFree(options->cells); options->cells = NULL;

    // Prepare for combination
    if (!ppStackCombinePrepare(stack, options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to prepare for combination.");
        psFree(stack);
        psFree(options);
        return false;
    }

    // Initial combination
    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
    if (!ppStackCombineInitial(stack, options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to perform initial combination.");
        psFree(stack);
        psFree(options);
        return false;
    }
    ppStackMemDump("convolve");
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 3: Make Initial Stack: %f sec", psTimerClear("PPSTACK_STEPS"));


    // Pixel rejection
    psTrace("ppStack", 1, "Reject pixels....\n");
    if (!ppStackReject(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to reject pixels.");
        psFree(stack);
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("reject");


    // Final combination
    psTrace("ppStack", 2, "Final stack of convolved images....\n");
    if (!ppStackCombineFinal(stack, options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to perform final combination.");
        psFree(stack);
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("final");


    // Clean up
    psTrace("ppStack", 2, "Cleaning up after combination....\n");
    if (!ppStackCleanup(stack, options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to clean up.");
        psFree(stack);
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6: WCS & JPEGS: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("cleanup");

    psFree(stack);


    // Photometry
    psTrace("ppStack", 1, "Photometering stacked image....\n");
    if (!ppStackPhotometry(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry.");
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("photometry");


    // Finish up
    psTrace("ppStack", 1, "Finishing up....\n");
    if (!ppStackFinish(options, config)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to finish up.");
        psFree(options);
        return false;
    }
    psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Final output: %f sec", psTimerClear("PPSTACK_STEPS"));
    ppStackMemDump("finish");

    psFree(options);
    return true;
}
