/** @file  pmMaskBadPixels.c
 *
 *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
 *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
 *  match the value to mask.
 *
 *  Apply the supplied bad pixel mask readout (mask) to the given science readout (input).
 *  The science readout must already have a supplied mask element (use eg. pmReadoutSetMask)
 *  The supplied mask image must be of MASK type
 
 *  If maskVal is non-zero, all pixels in the mask have any of the same bits sets as maskVal
 *  shall have the corresponding bits raised.
 
 *  If maskVal is zero, any zero pixels in the mask be OR-ed with PM_MASK_BAD 
 
 * Note that the input image and the mask need not be the same size, since the input image may
 * already have been trimmed (following overscan subtraction).  The function assumes the pixels
 * originate from the same detector and uses the values of col0,row0 in both the input and the
 * mask to match corresponding pixels.
 
 * In the event that the mask image is too small (i.e., pixels on the input image refer to
 * pixels outside the range of the mask image), the function shall generate an error.
 
 *  @author Ross Harman, MHPCC
 *  @author Eugene Magnier, IfA
 *
 *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-06-10 02:59:23 $
 *
 *  Copyright 2004 IfA, University of Hawaii
 */

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

#include <stdio.h>
#include <math.h>
#include <strings.h>

#include "pmFPAMaskWeight.h"
#include "pmMaskBadPixels.h"
#include "pmMaskBadPixelsErrors.h"

// apply an externally-supplied mask image to the current science image
// this function
bool pmMaskBadPixels(pmReadout *input, pmReadout *mask, psMaskType maskVal)
{

    int xI;
    int xJ;

    PS_ASSERT_PTR_NON_NULL(input, false);
    PS_ASSERT_PTR_NON_NULL(input->mask, false);
    PS_ASSERT_IMAGE_TYPE(input->mask, PS_TYPE_MASK, NULL);

    PS_ASSERT_PTR_NON_NULL(mask, false);
    PS_ASSERT_PTR_NON_NULL(mask->mask, false);
    PS_ASSERT_IMAGE_TYPE(mask->mask, PS_TYPE_MASK, NULL);

    psImage *inMask = input->mask;
    psImage *exMask = mask->mask;

    int rowMax = inMask->row0 + inMask->numRows;
    int colMax = inMask->col0 + inMask->numCols;

    if (exMask->row0 > inMask->row0) {
        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
                 inMask->numRows, inMask->numCols, exMask->numRows, exMask->numCols);
        return false;
    }
    if (exMask->col0 > inMask->col0) {
        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
                 inMask->numRows, inMask->numCols, exMask->numRows, exMask->numCols);
        return false;
    }
    if (exMask->row0 + exMask->numRows < rowMax) {
        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
                 inMask->numRows, inMask->numCols, exMask->numRows, exMask->numCols);
        return false;
    }
    if (exMask->col0 + exMask->numCols < colMax) {
        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
                 inMask->numRows, inMask->numCols, exMask->numRows, exMask->numCols);
        return false;
    }

    // Determine total offset based on image offset with chip offset
    // XXX if we choose to correct for the readout location, apply input->col0,row0 here
    int offCol = inMask->col0 - exMask->col0;
    int offRow = inMask->row0 - exMask->row0;

    // masks are both of type PS_TYPE_MASK
    psMaskType **exVal = exMask->data.U8;
    psMaskType **inVal = inMask->data.U8;

    // apply exMask values
    if (maskVal) {
        // set raised pixels in exMask which are selected by maskVal
        for (int j = 0; j < inMask->numRows; j++) {
            xJ = j - offRow;
            for (int i = 0; i < inMask->numCols; i++) {
                xI = i - offCol;
                inVal[j][i] |= (maskVal & exVal[xJ][xI]);
            }
        }
    } else {
        // set raised pixels in exMask which are selected by maskVal
        for (int j = 0; j < inMask->numRows; j++) {
            xJ = j - offRow;
            for (int i = 0; i < inMask->numCols; i++) {
                xI = i - offCol;
                if (exVal[xJ][xI] == 0) {
                    inVal[j][i] |= PM_MASK_BAD;
                }
            }
        }
    }

    return true;
}
