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

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

#include "ppSub.h"

bool ppSubCamera(pmConfig *config)
{
    bool status = false;                // Status of definition

    // Input image
    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PPSUB.INPUT", "INPUT");
    if (!status || !input) {
        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.INPUT");
        return false;
    }
    if (input->type != PM_FPA_FILE_IMAGE) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT is not of type IMAGE");
        return false;
    }

    // Input mask
    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.MASK", "INPUT.MASK");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.MASK");
        return NULL;
    }
    if (inputMask && inputMask->type != PM_FPA_FILE_MASK) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT.MASK is not of type MASK");
        return false;
    }

    // Input weight map
    pmFPAfile *inputWeight = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.WEIGHT", "INPUT.WEIGHT");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.WEIGHT");
        return NULL;
    }
    if (inputWeight && inputWeight->type != PM_FPA_FILE_WEIGHT) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT.WEIGHT is not of type WEIGHT");
        return false;
    }

    // Reference image
    status = false;
    pmFPAfile *ref = pmFPAfileDefineFromArgs(&status, config, "PPSUB.REF", "REF");
    if (!ref) {
        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.REF");
        return false;
    }
    if (ref->type != PM_FPA_FILE_IMAGE) {
        psError(PS_ERR_IO, true, "PPSUB.REF is not of type IMAGE");
        return false;
    }

    // Reference mask
    pmFPAfile *refMask = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.MASK", "REF.MASK");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.MASK");
        return NULL;
    }
    if (refMask && refMask->type != PM_FPA_FILE_MASK) {
        psError(PS_ERR_IO, true, "PPSUB.REF.MASK is not of type MASK");
        return false;
    }

    // Reference weight map
    pmFPAfile *refWeight = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.WEIGHT", "REF.WEIGHT");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.WEIGHT");
        return NULL;
    }
    if (refWeight && refWeight->type != PM_FPA_FILE_WEIGHT) {
        psError(PS_ERR_IO, true, "PPSUB.REF.WEIGHT is not of type WEIGHT");
        return false;
    }

    // Output image
    pmFPAfile *output = pmFPAfileDefineFromFile(config, input, 1, 1, "PPSUB.OUTPUT");
    if (!output) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT"));
        return false;
    }
    if (output->type != PM_FPA_FILE_IMAGE) {
        psError(PS_ERR_IO, true, "PPSUB.OUTPUT is not of type IMAGE");
        return false;
    }
    output->save = true;

    // Output mask
    pmFPAfile *outMask = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.MASK");
    if (!outMask) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.MASK"));
        return false;
    }
    if (outMask->type != PM_FPA_FILE_MASK) {
        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.MASK is not of type MASK");
        return false;
    }
    outMask->save = true;

    // Output weight
    if (inputWeight && refWeight) {
        pmFPAfile *outWeight = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.WEIGHT");
        if (!outWeight) {
            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.WEIGHT"));
            return false;
        }
        if (outWeight->type != PM_FPA_FILE_WEIGHT) {
            psError(PS_ERR_IO, true, "PPSUB.OUTPUT.WEIGHT is not of type WEIGHT");
            return false;
        }
        outWeight->save = true;
    }

    // Output JPEGs
    pmFPAfile *jpeg1 = pmFPAfileDefineOutput(config, NULL, "PPSUB.OUTPUT.JPEG1");
    if (!jpeg1) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.JPEG1"));
        return false;
    }
    if (jpeg1->type != PM_FPA_FILE_JPEG) {
        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.JPEG1 is not of type JPEG");
        return false;
    }
    jpeg1->save = true;
    pmFPAfile *jpeg2 = pmFPAfileDefineOutput(config, NULL, "PPSUB.OUTPUT.JPEG2");
    if (!jpeg2) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.JPEG2"));
        return false;
    }
    if (jpeg2->type != PM_FPA_FILE_JPEG) {
        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.JPEG2 is not of type JPEG");
        return false;
    }
    jpeg2->save = true;

    // Output subtraction kernel
    pmFPAfile *outKernels = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.KERNELS");
    if (!outKernels) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.KERNELS"));
        return false;
    }
    if (outKernels->type != PM_FPA_FILE_SUBKERNEL) {
        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.KERNELS is not of type SUBKERNEL");
        return false;
    }
    outKernels->save = true;

#if 0
    if (!pmFPAAddSourceFromFormat(output->fpa, "Subtraction", output->format)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
        return false;
    }
#endif

    pmFPAfile *sources = pmFPAfileDefineFromArgs(&status, config, "PPSUB.SOURCES", "PPSUB.SOURCES");
    if (!status) {
        psError(PS_ERR_IO, false, "Failed to load file definition PPSUB.SOURCES");
        return false;
    }
    if (sources && sources->type != PM_FPA_FILE_CMF) {
        psError(PS_ERR_IO, true, "PPSUB.SOURCES is not of type CMF");
        return false;
    }

    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
    if (!recipe) {
        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSUB_RECIPE);
        return false;
    }

    // psPhot input
    if (psMetadataLookupBool(NULL, recipe, "PHOTOMETRY")) {
        psphotModelClassInit ();        // load implementation-specific models
        pmFPAfile *psphot = pmFPAfileDefineFromFPA(config, output->fpa, 1, 1, "PSPHOT.INPUT");
        if (!psphot) {
            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.INPUT");
            return false;
        }
        if (psphot->type != PM_FPA_FILE_IMAGE) {
            psError(PS_ERR_IO, true, "PSPHOT.INPUT is not of type IMAGE");
            return false;
        }

        if (!psphotDefineFiles(config, psphot)) {
            psError(PS_ERR_UNKNOWN, false, "Unable to set up psphot files.");
            return false;
        }

        // Internal-ish file for getting the PSF from the matched addition
        pmFPAfile *psf = pmFPAfileDefineOutputFromFile(config, output, "PSPHOT.PSF.LOAD");
        if (!psf) {
            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.PSF.LOAD");
            return false;
        }
        if (psf->type != PM_FPA_FILE_PSF) {
            psError(PS_ERR_IO, true, "PSPHOT.PSF.LOAD is not of type PSF");
            return false;
        }
        pmFPAfileActivate(config->files, false, "PSPHOT.PSF.LOAD");

    }

    return true;
}
