/** @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"

// Define an input file
static pmFPAfile *defineInputFile(pmConfig *config,// Configuration
                                  pmFPAfile *bind,    // File to which to bind, or NULL
                                  char *filerule,     // Name of file rule
                                  char *argname,      // Argument name
                                  pmFPAfileType fileType // Type of file
    )
{
    bool status;

    // look for the file on the RUN metadata
    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
    if (!status) {
        psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
        return NULL;
    }
    if (!file) {
        // look for the file on the argument list
        if (bind) {
            file = pmFPAfileBindFromArgs(&status, bind, config, filerule, argname);
        } else {
            file = pmFPAfileDefineFromArgs(&status, config, filerule, argname);
        }
        if (!status) {
            psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
            return false;
        }
    }

    if (!file) {
        return NULL;
    }

    if (file->type != fileType) {
        psError(PS_ERR_UNKNOWN, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
        return NULL;
    }

    return file;
}

// Define an output file
static pmFPAfile *defineOutputFile(pmConfig *config, // Configuration
                                   pmFPAfile *template,    // File to use as basis for definition
                                   char *filerule,     // Name of file rule
                                   pmFPAfileType fileType // Type of file
    )
{

    pmFPAfile *file = pmFPAfileDefineFromFile(config, template, 1, 1, filerule);
    if (!file) {
        psError(PS_ERR_IO, false, _("Unable to generate output file from %s"), filerule);
        return NULL;
    }
    if (file->type != fileType) {
        psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
        return NULL;
    }

    return file;
}


// Define an output file that will be used in a calculation
// This means it might already be available in the RUN metadata
static pmFPAfile *defineCalcFile(pmConfig *config, // Configuration
                                 pmFPAfile *bind,    // File to which to bind, or NULL
                                 char *filerule,     // Name of file rule
                                 pmFPAfileType fileType // Type of file
    )
{
    bool status;

    // look for the file on the RUN metadata
    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
    if (!status) {
        psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
        return NULL;
    }
    if (file) {
        // It's an input
        file->save = false;
    }

    // define new version of file
    if (!file) {
        pmFPAfileDefineOutput(config, bind ? bind->fpa : NULL, filerule);
        if (!status) {
            psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
            return false;
        }
        if (file) {
            // It's an output
            file->save = true;
        }
    }

    if (!file) {
        return NULL;
    }

    if (file->type != fileType) {
        psError(PS_ERR_UNKNOWN, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
        return NULL;
    }

    return file;
}


bool ppSubCamera(pmConfig *config)
{
    psAssert(config, "Require configuration");

    // Input image
    pmFPAfile *input = defineInputFile(config, NULL, "PPSUB.INPUT", "INPUT", PM_FPA_FILE_IMAGE);
    if (!input) {
        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.INPUT");
        return false;
    }
    defineInputFile(config, input, "PPSUB.INPUT.MASK", "INPUT.MASK", PM_FPA_FILE_MASK);
    pmFPAfile *inVar = defineInputFile(config, input, "PPSUB.INPUT.VARIANCE", "INPUT.VARIANCE",
                                       PM_FPA_FILE_VARIANCE);
    defineInputFile(config, input, "PPSUB.INPUT.SOURCES", "INPUT.SOURCES", PM_FPA_FILE_CMF);

    // Reference image
    pmFPAfile *ref = defineInputFile(config, NULL, "PPSUB.REF", "REF", PM_FPA_FILE_IMAGE);
    if (!ref) {
        psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.REF");
        return false;
    }
    defineInputFile(config, ref, "PPSUB.REF.MASK", "REF.MASK", PM_FPA_FILE_MASK);
    pmFPAfile *refVar = defineInputFile(config, ref, "PPSUB.REF.VARIANCE", "REF.VARIANCE",
                                        PM_FPA_FILE_VARIANCE);
    defineInputFile(config, ref, "PPSUB.REF.SOURCES", "REF.SOURCES", PM_FPA_FILE_CMF);


    // Output image
    pmFPAfile *output = defineOutputFile(config, input, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
    pmFPAfile *outMask = defineOutputFile(config, output, "PPSUB.OUTPUT.MASK", PM_FPA_FILE_MASK);
    if (!output || !outMask) {
        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
        return false;
    }
    output->save = true;
    outMask->save = true;
    pmFPAfile *outVar = NULL;
    if (inVar && refVar) {
        outVar = defineOutputFile(config, output, "PPSUB.OUTPUT.VARIANCE", PM_FPA_FILE_VARIANCE);
        if (!outVar) {
            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
            return false;
        }
        outVar->save = true;
    }


    // Convolved input image
    pmFPAfile *inConvImage = defineOutputFile(config, input, "PPSUB.INPUT.CONV", PM_FPA_FILE_IMAGE);
    pmFPAfile *inConvMask = defineOutputFile(config, input, "PPSUB.INPUT.CONV.MASK", PM_FPA_FILE_MASK);
    if (!inConvImage || !inConvMask) {
        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
        return false;
    }
    if (inVar) {
        pmFPAfile *inConvVar = defineOutputFile(config, input, "PPSUB.INPUT.CONV.VARIANCE",
                                                PM_FPA_FILE_VARIANCE);
        if (!inConvVar) {
            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
            return false;
        }
    }

    // Convolved ref image
    pmFPAfile *refConvImage = defineOutputFile(config, input, "PPSUB.REF.CONV", PM_FPA_FILE_IMAGE);
    pmFPAfile *refConvMask = defineOutputFile(config, input, "PPSUB.REF.CONV.MASK", PM_FPA_FILE_MASK);
    if (!refConvImage || !refConvMask) {
        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
        return false;
    }
    if (refVar) {
        pmFPAfile *refConvVar = defineOutputFile(config, input, "PPSUB.REF.CONV.VARIANCE",
                                                 PM_FPA_FILE_VARIANCE);
        if (!refConvVar) {
            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
            return false;
        }
    }


    // 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 *kernel = defineCalcFile(config, output, "PPSUB.OUTPUT.KERNELS", PM_FPA_FILE_SUBKERNEL);
    if (!kernel) {
        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to define file PPSUB.OUTPUT.KERNELS");
        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;
}
