#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 = pmFPAfileDefineSkycell(config, NULL, "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 = pmFPAfileDefineSkycell(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 = pmFPAfileDefineSkycell(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;
    }

    pmFPAview *view = pmFPAviewAlloc(0);// View to PHU
    const char *name = psMetadataLookupStr(NULL, input->fpa->concepts, "FPA.NAME");
    if (!pmFPAAddSourceFromView(output->fpa, name, view, output->format)) {
        psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to output.");
        psFree(view);
        return false;
    }
    psFree(view);

    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;
    }

    // psPhot input
    if (psMetadataLookup(config->arguments, "PSPHOT.PSF")) {
        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 (input->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;
        }
    }

    return true;
}
