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

#include "ppImage.h"

#define ESCAPE(STATUS,...) {                    \
        psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__);   \
        psFree(view);                                   \
        return false;                                   \
    }

static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipename, const char *recipevalue);

bool ppImageDetrendPatternApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView,
                                const ppImageOptions *options)
{
    pmCell *cell = NULL;

    assert(options->doPatternRow || options->doPatternCell); // do not call if not needed
    assert(inputView->chip != -1);
    assert(inputView->cell == -1);
    assert(inputView->readout == -1);

    if (options->doPatternRow) {
        bool status;
        pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
        pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
        *view = *inputView;
        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
            if (!cell->process || !cell->file_exists) {
                continue;
            }
            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
              ESCAPE(false, "load failure for Cell");
            }

            if (!cell->data_exists) {
                continue;
            }

            if (cell->readouts->n > 1) {
                psWarning ("Skipping Video Cell for ppImageDetrendPatternApply");
                continue;
            }

            bool doPattern = false;
            if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.ROW.SUBSET")) {
                ESCAPE(false, "Unable to determine whether row pattern matching should be applied.");
            }
            if (!doPattern) continue;

            const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
            const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
            psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s\n",
                     chipName, cellName);

            // process each of the readouts
            pmReadout *readout;         // Readout from cell
            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                    ESCAPE(false, "load failure for Readout");
                }
                if (!readout->data_exists) {
                    continue;
                }

                // Perform pattern correction
                if (!pmPatternRow(readout, options->patternRowOrder, options->patternRowIter,
                                  options->patternRowRej, options->patternRowThresh, options->patternRowMean,
                                  options->patternRowStdev, options->maskValue, options->darkMask)) {
                    psFree(view);
                    return(false);
                }
            }
        }
        psFree(view);
    }

    if (options->doPatternCell) {
        int numCells = chip->cells->n;       // Number of cells
        psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell?
        pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
        *view = *inputView;
        for (int i = 0; i < chip->cells->n; i++) {
            view->cell = i;

            pmCell *cell = chip->cells->data[i]; // Cell of interest
            if (cell->readouts->n > 1) {
                psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell pattern correction on video cell.");
                continue;
            }

            bool doPattern = false;
            if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CELL.SUBSET")) {
                ESCAPE(false, "Unable to determine whether row pattern matching should be applied.");
            }
            if (doPattern) {
                tweak->data.U8[i] = 0xFF;
            }
        }

        // Tweak the cells
        if (!pmPatternCell(chip, tweak, options->patternCellBG, options->patternCellMean,
                           options->maskValue, options->darkMask)) {
            psFree(tweak);
            psFree(view);
            return false;
        }
        psFree(tweak);
        psFree(view);
    }

    return(true);
}

static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue) {

    *doit = false;

    psMetadataItem *doPattern = pmConfigRecipeValueByView(config, recipeName, recipeValue, chip->parent, view);
    if (!doPattern) {
        psError(PS_ERR_UNKNOWN, false, "Unable to determine whether row pattern matching should be applied.");
        return false;
    }
    if (doPattern->type == PS_DATA_BOOL) {
        *doit = doPattern->data.B;
        return true;
    }
    if (doPattern->type == PS_DATA_STRING) {
        // expect a string of the form "000110001001" with at least view->cell entries
        char *string = doPattern->data.str;
        if (strlen(string) < view->cell) {
            psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string (too few elements %d)", (int) strlen(string));
            return false;
        }
        switch (string[view->cell]) {
          case '0':
          case 'f':
          case 'F':
          case 'n':
          case 'N':
            *doit = false;
            return true;
          case '1':
          case 't':
          case 'T':
          case 'y':
          case 'Y':
            *doit = true;
            return true;
          default:
            psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string %s (unknown value %c))", string, string[view->cell]);
            return false;
        }
        psAbort("imposible to reach here");
    }
    psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET : invalid data type");
    return false;
}
