/** @file ppSubCamera.c
 *
 *  @brief
 *
 *  @ingroup ppSub
 *
 *  @author IfA
 *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2009-02-18 00:31:20 $
 *  Copyright 2009 Institute for Astronomy, University of Hawaii
 */

#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;                // result from pmFPAfileDefine operations

    // 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 variance map
    pmFPAfile *inputVariance = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.VARIANCE", "INPUT.VARIANCE");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.VARIANCE");
        return NULL;
    }
    if (inputVariance && inputVariance->type != PM_FPA_FILE_VARIANCE) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT.VARIANCE is not of type VARIANCE");
        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 variance map
    pmFPAfile *refVariance = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.VARIANCE", "REF.VARIANCE");
    if (!status) {
        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.VARIANCE");
        return NULL;
    }
    if (refVariance && refVariance->type != PM_FPA_FILE_VARIANCE) {
        psError(PS_ERR_IO, true, "PPSUB.REF.VARIANCE is not of type VARIANCE");
        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 variance
    if (inputVariance && refVariance) {
        pmFPAfile *outVariance = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.VARIANCE");
        if (!outVariance) {
            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
            return false;
        }
        if (outVariance->type != PM_FPA_FILE_VARIANCE) {
            psError(PS_ERR_IO, true, "PPSUB.OUTPUT.VARIANCE is not of type VARIANCE");
            return false;
        }
        outVariance->save = true;
    }

    // Convolved input image
    pmFPAfile *inConv = pmFPAfileDefineFromFile(config, input, 1, 1, "PPSUB.INPUT.CONV");
    if (!inConv) {
        psError(PS_ERR_IO, false, _("Unable to generate output file for PPSUB.INPUT.CONV"));
        return false;
    }
    if (output->type != PM_FPA_FILE_IMAGE) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV is not of type IMAGE");
        return false;
    }
    // XXX should be based on recipe : inConv->save = true;

    // Convolved input mask
    pmFPAfile *inConvMask = pmFPAfileDefineOutput(config, inConv->fpa, "PPSUB.INPUT.CONV.MASK");
    if (!inConvMask) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.INPUT.CONV.MASK"));
        return false;
    }
    if (inConvMask->type != PM_FPA_FILE_MASK) {
        psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV.MASK is not of type MASK");
        return false;
    }
    // XXX should be based on recipe : inConvMask->save = true;

    // Convolved input variance
    if (inputVariance) {
        pmFPAfile *inConvVariance = pmFPAfileDefineOutput(config, inConv->fpa, "PPSUB.INPUT.CONV.VARIANCE");
        if (!inConvVariance) {
            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
            return false;
        }
        if (inConvVariance->type != PM_FPA_FILE_VARIANCE) {
            psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV.VARIANCE is not of type VARIANCE");
            return false;
        }
        // XXX should be based on recipe : inConvVariance->save = true;
    }

    // Convolved ref image
    pmFPAfile *refConv = pmFPAfileDefineFromFile(config, ref, 1, 1, "PPSUB.REF.CONV");
    if (!refConv) {
        psError(PS_ERR_IO, false, _("Unable to generate output file for PPSUB.REF.CONV"));
        return false;
    }
    if (output->type != PM_FPA_FILE_IMAGE) {
        psError(PS_ERR_IO, true, "PPSUB.REF.CONV is not of type IMAGE");
        return false;
    }
    // XXX should be based on recipe : refConv->save = true;

    // Convolved ref mask
    pmFPAfile *refConvMask = pmFPAfileDefineOutput(config, refConv->fpa, "PPSUB.REF.CONV.MASK");
    if (!refConvMask) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.REF.CONV.MASK"));
        return false;
    }
    if (refConvMask->type != PM_FPA_FILE_MASK) {
        psError(PS_ERR_IO, true, "PPSUB.REF.CONV.MASK is not of type MASK");
        return false;
    }
    // XXX should be based on recipe : refConvMask->save = true;

    // Convolved ref variance
    if (refVariance) {
        pmFPAfile *refConvVariance = pmFPAfileDefineOutput(config, refConv->fpa, "PPSUB.REF.CONV.VARIANCE");
        if (!refConvVariance) {
            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
            return false;
        }
        if (refConvVariance->type != PM_FPA_FILE_VARIANCE) {
            psError(PS_ERR_IO, true, "PPSUB.REF.CONV.VARIANCE is not of type VARIANCE");
            return false;
        }
        // XXX should be based on recipe : refConvVariance->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

        // Internal-ish file for getting the PSF from the minuend
        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");

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

    return true;
}
