/** @file  pmFPA_JPEG.c
 *
 * This file contains functions to write JPEG images.
 *
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.23 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2007-09-21 00:02:11 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

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

/*****************************************************************************/
/* INCLUDE FILES                                                             */
/*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <strings.h>            /* for strn?casecmp */
#include <pslib.h>

#include "pmConfig.h"
#include "pmDetrendDB.h"

#include "pmHDU.h"
#include "pmFPA.h"
#include "pmFPALevel.h"
#include "pmFPAview.h"
#include "pmFPAfile.h"
#include "pmFPA_JPEG.h"


bool pmFPAviewWriteJPEG(const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
{
    PS_ASSERT_PTR_NON_NULL(view, false);
    PS_ASSERT_PTR_NON_NULL(file, false);

    pmFPA *fpa = file->fpa;

    if (view->chip == -1) {
        pmFPAWriteJPEG (fpa, view, file, config);
        return true;
    }

    if (view->chip >= fpa->chips->n) {
        return false;
    }
    pmChip *chip = fpa->chips->data[view->chip];

    if (view->cell == -1) {
        pmChipWriteJPEG (chip, view, file, config);
        return true;
    }

    if (view->cell >= chip->cells->n) {
        return false;
    }
    pmCell *cell = chip->cells->data[view->cell];

    if (view->readout == -1) {
        pmCellWriteJPEG (cell, view, file, config);
        return true;
    }

    if (view->readout >= cell->readouts->n) {
        return false;
    }
    pmReadout *readout = cell->readouts->data[view->readout];

    pmReadoutWriteJPEG (readout, view, file, config);
    return true;
}

// read in all chip-level JPEG files for this FPA
bool pmFPAWriteJPEG (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
{
    PS_ASSERT_PTR_NON_NULL(fpa, false);
    PS_ASSERT_PTR_NON_NULL(view, false);
    PS_ASSERT_PTR_NON_NULL(file, false);

    for (int i = 0; i < fpa->chips->n; i++) {

        pmChip *chip = fpa->chips->data[i];
        pmChipWriteJPEG (chip, view, file, config);
    }
    return true;
}

// read in all cell-level JPEG files for this chip
bool pmChipWriteJPEG (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
{
    PS_ASSERT_PTR_NON_NULL(chip, false);
    PS_ASSERT_PTR_NON_NULL(view, false);
    PS_ASSERT_PTR_NON_NULL(file, false);

    for (int i = 0; i < chip->cells->n; i++) {

        pmCell *cell = chip->cells->data[i];
        pmCellWriteJPEG (cell, view, file, config);
    }
    return true;
}

// read in all readout-level JPEG files for this cell
bool pmCellWriteJPEG (pmCell *cell, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
{
    PS_ASSERT_PTR_NON_NULL(cell, false);
    PS_ASSERT_PTR_NON_NULL(view, false);
    PS_ASSERT_PTR_NON_NULL(file, false);

    for (int i = 0; i < cell->readouts->n; i++) {

        pmReadout *readout = cell->readouts->data[i];
        pmReadoutWriteJPEG (readout, view, file, config);
    }
    return true;
}

// write JPEG image for readout
// note that this function will try as hard a possible to write out a jpeg.  it will not fail
// just because the values are in a poor range.  it is more convenient to know you are getting
// a jpeg which is weird than to fail to get the file at all.
bool pmReadoutWriteJPEG (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
{
    bool status;
    PS_ASSERT_PTR_NON_NULL(readout, false);
    PS_ASSERT_PTR_NON_NULL(view, false);
    PS_ASSERT_PTR_NON_NULL(file, false);

    if (file->type != PM_FPA_FILE_JPEG) {
        psError(PS_ERR_UNKNOWN, true, "warning: type mismatch");
        return false;
    }

    psTrace("psModules.camera", 3, "writing jpeg for readout %zd\n", (size_t) readout);

    psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PPIMAGE");
    if (!status) {
        psError(PS_ERR_UNKNOWN, true, "missing recipe PPIMAGE in config data");
        return false;
    }

    psMetadata *options = psMetadataLookupMetadata(&status, recipe, file->name);
    if (!status) {
        psError(PS_ERR_UNKNOWN, true, "missing %s options in PPIMAGE recipe", file->name);
        return false;
    }

    float min = 0;
    float max = 0;                 // Minimum and maximum for stretch
    float mean = 0;
    float delta = 0;

    // measure the image statistics for scaling
    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    stats->nSubsample = 10000;
    if (!psImageBackground(stats, NULL, readout->image, NULL, 0, rng)) {
        psStats *statsAlt = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
        stats->nSubsample = 10000;
        if (!psImageBackground(stats, NULL, readout->image, NULL, 0, rng)) {
            psLogMsg("jpeg", PS_LOG_WARN, "Unable to measure statistics for image, writing blank jpeg\n");
            mean = 0;
            delta = 1;
        } else {
            mean = statsAlt->sampleMean;
            delta = 2*statsAlt->sampleStdev;
        }
        psFree(statsAlt);
    } else {
        mean = stats->robustMedian;
        delta = stats->robustUQ - stats->robustLQ;
    }
    psFree(rng);
    psFree(stats);

    char *colormapName = psMemIncrRefCounter (psMetadataLookupStr (NULL, options, "COLORMAP"));
    if (!colormapName)
        colormapName = psStringCopy ("-greyscale");
    char *mode = psMemIncrRefCounter (psMetadataLookupStr (NULL, options, "SCALE.MODE"));
    if (!mode)
        mode = psStringCopy ("RANGE");
    float fmin = psMetadataLookupF32 (&status, options, "SCALE.MIN");
    if (!status)
        fmin = -3.0;
    float fmax = psMetadataLookupF32 (&status, options, "SCALE.MAX");
    if (!status)
        fmax = +6.0;

    if (!strcasecmp (mode, "RANGE")) {
        min = mean + fmin*delta;
        max = mean + fmax*delta;
    }
    if (!strcasecmp (mode, "FRACTION")) {
        min = fmin*mean;
        max = fmax*mean;
    }
    if (!strcasecmp (mode, "VALUE")) {
        min = fmin;
        max = fmax;
    }

    if (!isfinite(min) || !isfinite(max)) {
        psLogMsg("jpeg", PS_LOG_WARN, "The stretch parameters are not both finite, writing blank jpeg\n");
        min = 0;
        max = 1;
    }

    psImageJpegColormap *map = psImageJpegColormapSet (NULL, colormapName);
    if (!map) {
        map = psImageJpegColormapSet (NULL, "-greyscale");
    }

    psImageJpeg (map, readout->image, file->filename, min, max);

    psFree(map);
    psFree(colormapName);
    psFree(mode);
    return true;
}
