/* $Id: casu_mask.c,v 1.4 2015/09/14 18:47:00 jim Exp $
 *
 * This file is part of the CASU Pipeline utilities
 * Copyright (C) 2015 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: jim $
 * $Date: 2015/09/14 18:47:00 $
 * $Revision: 1.4 $
 * $Name:  $
 */

/* Includes */

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <cpl.h>
#include "casu_utils.h"
#include "casu_fits.h"
#include "casu_mask.h"

static unsigned char *casu_mask_getbpm(casu_fits *bpmimage);
static unsigned char *casu_mask_conf2bpm(casu_fits *cpmimage);

/**
    \defgroup casu_mask casu_mask
    \ingroup casu_routines

    \brief
    These are methods for manipulating the casu_mask bad pixel mask objects

    \author
    Jim Lewis, CASU
*/

/**@{*/

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_define
    \par Purpose:
        Define a mask type from a given input frameset
    \par Description
        An input frameset is searched for either a master bad pixel mask
        or a master confidence map. (BPM is searched for first). If one of
        them is found then the frame is returned along with a flag defining
        the mask type.
    \par Language:
        C
    \param framelist
        The input frame list
    \param labels
        The input frame list labels
    \param nlab
        The number of labels
    \param conftag
        The tag for a confidence map
    \param bpmtag
        The tag for a bad pixel mask
    \return 
        The mask structure
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern casu_mask *casu_mask_define(cpl_frameset *framelist, cpl_size *labels, 
                                   cpl_size nlab, const char *conftag, 
                                   const char *bpmtag) {
    cpl_frame *master_mask;
    int masktype;
    casu_mask *m;
    const char *fctid = "casu_mask_define";

    /* What kind of mask are we defining here? */

    if ((master_mask = casu_frameset_subgroup_1(framelist,labels,nlab,
                                                bpmtag)) == NULL) {
        if ((master_mask = casu_frameset_subgroup_1(framelist,labels,nlab,
                                                    conftag)) == NULL) {
            cpl_msg_info(fctid,
                         "No master pixel mask found -- all pixels considered good");
            masktype = MASK_NONE;
        } else {
            masktype = MASK_CPM;
        }
    } else {
        masktype = MASK_BPM;
    }

    /* If a master mask is defined, then check to see if the file is 
       accessible. If it isn't then continue on as though you don't have one */

    if (master_mask != NULL) {
        if (access(cpl_frame_get_filename(master_mask),R_OK) != 0) {
            cpl_msg_warning(fctid,"File %s is not read accessible",
                            cpl_frame_get_filename(master_mask));
            masktype = MASK_NONE;
            freeframe(master_mask);
        }
    }

    /* Get the casu_mask structure... */

    m = cpl_malloc(sizeof(*m));

    /* Initialise a few things */

    m->master_mask = master_mask;
    m->mask_image = NULL;
    m->masktype = masktype;
    m->nx = 0;
    m->ny = 0;
    m->mdata = NULL;

    /* Now return it */

    return(m);    
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_objmask_define
    \par Purpose:
        Define a mask structure from an object mask frame
    \par Description
        A mask structure is defined from an input object mask frame. These
        behave just like a bad pixel mask.
    \par Language:
        C
    \param frame
        The input frame
    \return 
        The mask structure
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern casu_mask *casu_objmask_define(cpl_frame *frame) {
    casu_mask *m;

    /* If there isn't one then send back a NULL */

    if (frame == NULL)
        return(NULL);

    /* Get the casu_mask structure... */

    m = cpl_malloc(sizeof(*m));

    /* Initialise a few things */

    m->master_mask = cpl_frame_duplicate(frame);
    m->mask_image = NULL;
    m->masktype = MASK_OPM;
    m->nx = 0;
    m->ny = 0;
    m->mdata = NULL;

    /* Now return it */

    return(m);    
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_load
    \par Purpose:
        Load the image from an input mask object
    \par Description:
        The image from an input casu_mask object is loaded
    \par Language:
        C
    \param m
        The input mask object
    \param nexten
        The image extension that you want to load.
    \param nx
        The X dimension of the data array (in case the image is undefined)
    \param ny
        The Y dimension of the data array (in case the image is undefined)
    \return
        The usual casu status variable
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern int casu_mask_load(casu_mask *m, int nexten, int nx, int ny) {

    /* Check for nonsense input */

    if (m == NULL)
        return(CASU_FATAL);

    /* Look to see if the sizes make sense */

    if (nx <= 0 && ny <= 0 && m->masktype == MASK_NONE)
        return(CASU_FATAL);

    /* If the mask image has already been loaded then free it up. */

    if (m->mask_image != NULL) {
        casu_fits_delete(m->mask_image);
        freespace(m->mdata);
    }

    /* Load up the image if there is one. */

    if (m->masktype != MASK_NONE) {
        if (m->masktype == MASK_CPM)
            m->mask_image = casu_fits_load(m->master_mask,CPL_TYPE_UNSPECIFIED,
                                           nexten);
        else
            m->mask_image = casu_fits_load(m->master_mask,CPL_TYPE_INT,nexten);
        if (m->mask_image == NULL)
            return(CASU_FATAL);
        m->nx = (int)cpl_image_get_size_x(casu_fits_get_image(m->mask_image));
        m->ny = (int)cpl_image_get_size_y(casu_fits_get_image(m->mask_image));
    } else {
        m->nx = nx;
        m->ny = ny;
    }

    /* Return the status */

    return(CASU_OK);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_delete
    \par Purpose:
        Delete internal workspace associated with a mask object, then delete
        the object itself.
    \par Description:
        All of the internal workspace in a mask object is freed. The the 
        memory holding the mask object itself is freed.
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern void casu_mask_delete(casu_mask *m) {

    if (m == NULL)
        return;
    casu_mask_clear(m);
    freeframe(m->master_mask); 
    cpl_free(m);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_wrap_bpm
    \par Purpose:
        Wrap a bpm data array in a casu_mask structure
    \par Description:
        A skeletal casu_mask structure is created and a given bad pixel
        mask is copied to the data array
    \par Language:
        C
    \param inbpm
        The input bad pixel map
    \param nx
        The X dimension of the map
    \param ny
        The Y dimension of the map
    \return
        The input bad pixel mask wrapped as casu_mask structure
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern casu_mask *casu_mask_wrap_bpm(unsigned char *inbpm, int nx, int ny) {
    casu_mask *m;
    cpl_image *im;
    int *mdata,i;

    /* Get the casu_mask structure... */

    m = cpl_malloc(sizeof(*m));

    /* Create the image and copy the input bpm to it */

    im = cpl_image_new((cpl_size)nx,(cpl_size)ny,CPL_TYPE_INT);
    mdata = cpl_image_get_data_int(im);
    for (i = 0; i < nx*ny; i++)
        mdata[i] = (int)(inbpm[i]);

    /* Initialise a few things */

    m->master_mask = NULL;
    m->mask_image = casu_fits_wrap(im,NULL,NULL,NULL);
    m->masktype = MASK_BPM;
    m->nx = nx;
    m->ny = ny;
    m->mdata = inbpm;
    return(m);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_clear
    \par Purpose:
        Delete internal workspace associated with a mask object, but keep
        the object itself intact.
    \par Description:
        All of the internal workspace in a mask object is freed. The the 
        memory holding the mask object itself is left alone.
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern void casu_mask_clear(casu_mask *m) {
    if (m == NULL)
        return;

    freespace(m->mdata);
    freefits(m->mask_image);
    m->nx = 0;
    m->ny = 0;
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_force
    \par Purpose:
        Forces a predefined mask out of the structure to be replaced by
        a MASK_NONE type.
    \par Description:
        This routine is used as a catch-all in case there is a problem in 
        loading an extension of a mask with casu_mask_load. In that case
        calling this routine will force out the old mask definition and
        replace it with a safe MASK_NONE option.
    \par Language:
        C
    \param m
        The input casu_mask object
    \param nx
        The X size of the mask array
    \param ny
        The Y size of the mask array
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern void casu_mask_force(casu_mask *m, int nx, int ny) {
    if (m == NULL) 
        return;
    freespace(m->mdata);
    freefits(m->mask_image);
    freeframe(m->master_mask);
    m->masktype = MASK_NONE;
    m->nx = nx;
    m->ny = ny;
}
    
/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_fits
    \par Purpose:
        Get the casu_fits structure from a mask object
    \par Description:
        Get the casu_fits structure from a mask object
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The casu_fits structure from the mask object
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern casu_fits *casu_mask_get_fits(casu_mask *m) {
    return(m->mask_image);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_filename
    \par Purpose:
        Get the filename from a casu_mask structure
    \par Description:
        Get the filename from a casu_mask structure
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The char pointer to the file name
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern const char *casu_mask_get_filename(casu_mask *m) {
    if (m->master_mask != NULL) {
        return(cpl_frame_get_filename(m->master_mask));
    } else {
        return(NULL);
    }
}


/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_size_x
    \par Purpose:
        Get the x size of the mask data array
    \par Description:
        Get the x size of the mask data array
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The X size of the mask data array
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern int casu_mask_get_size_x(casu_mask *m) {
    return(m->nx);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_size_y
    \par Purpose:
        Get the y size of the mask data array
    \par Description:
        Get the y size of the mask data array
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The Y size of the mask data array
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern int casu_mask_get_size_y(casu_mask *m) {
    return(m->ny);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_type
    \par Purpose:
        Get the mask type
    \par Description:
        Get the mask type
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The mask type. See casu_mask.h for more details
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern int casu_mask_get_type(casu_mask *m) {
    return(m->masktype);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_get_data
    \par Purpose:
        Get the mask data array
    \par Description:
        Get the mask data array
    \par Language:
        C
    \param m
        The input casu_mask object
    \return
        The bad pixel mask data array
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

extern unsigned char *casu_mask_get_data(casu_mask *m) {
    unsigned char *bpm;
    long npix;

    /* Has this already been done? */

    if (m->mdata != NULL)
        return(m->mdata);

    /* Get the bpm depending on what type of input you have */

    switch (m->masktype) {
    case MASK_NONE:
        npix = (m->nx)*(m->ny);
        bpm = cpl_calloc(npix,sizeof(*bpm));
        break;
    case MASK_BPM:
        bpm = casu_mask_getbpm(casu_mask_get_fits(m));
        break;
    case MASK_OPM:
        bpm = casu_mask_getbpm(casu_mask_get_fits(m));
        break;
    case MASK_CPM:
        bpm = casu_mask_conf2bpm(casu_mask_get_fits(m));
        break;
    default:
        npix = (m->nx)*(m->ny);
        bpm = cpl_calloc(npix,sizeof(*bpm));
        break;
    }
    m->mdata = bpm;
    return(bpm);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_getbpm
    \par Purpose:
        Get the data from a bad pixel mask frame.
    \par Description:
        A bad pixel map frame is read and the data transfered to a byte
        data array. This is needed because CPL doesn't have an unsigned
        char data type. The returned array needs to be deallocated when you
        are finished with it.
    \par Language:
        C
    \param bpmimage
        The input frame with the bad pixel mask
    \return
        The output bad pixel mask data. This needs to be deallocated when
        you are finished with it.
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static unsigned char *casu_mask_getbpm(casu_fits *bpmimage) {
    long npts,i;
    int *bpmdata;
    cpl_image *b;
    unsigned char *bpm;

    /* Load the bad pixel map data */

    b = casu_fits_get_image(bpmimage);
    npts = cpl_image_get_size_x(b)*cpl_image_get_size_y(b);
    bpmdata = cpl_image_get_data(b);

    /* Get some space for the bad pixel mask and define it */

    bpm = cpl_malloc(npts*sizeof(*bpm));
    for (i = 0; i < npts; i++)
        bpm[i] = (unsigned char)bpmdata[i];

    /* Tidy and exit */

    return(bpm);
}
        
/*---------------------------------------------------------------------------*/
/**
    \par Name:
        casu_mask_conf2bpm
    \par Purpose:
        Convert confidence map data to a bad pixel mask
    \par Description:
        A confidence map is given and a bad pixel mask is created. The
        BPM is set at pixels where the input confidence is zero. The output
        BPM needs to be deallocated when you're finished with it
    \par Language:
        C
    \param cpmimage
        The input confidence map image
    \return
        The output bad pixel mask data array. This need to be deallocated
        when you're finished with it
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static unsigned char *casu_mask_conf2bpm(casu_fits *cpmimage) {
    long npts,i;
    int *cpmdata;
    cpl_image *c;
    unsigned char *bpm;

    /* Load the confidence map image and get its data */

    c = casu_fits_get_image(cpmimage);
    npts = cpl_image_get_size_x(c)*cpl_image_get_size_y(c);
    cpmdata = cpl_image_get_data(c);

    /* Get some space for the bad pixel mask and define it where the
       confidence map is zero */

    bpm = cpl_malloc(npts*sizeof(*bpm));
    for (i = 0; i < npts; i++)
        bpm[i] = (cpmdata[i] == 0);

    /* Tidy and exit */

    return(bpm);
}

/**@}*/

/*

$Log: casu_mask.c,v $
Revision 1.4  2015/09/14 18:47:00  jim
replaced a call to free with cpl_free

Revision 1.3  2015/08/07 13:06:54  jim
Fixed copyright to ESO

Revision 1.2  2015/08/06 05:34:02  jim
Fixes to get rid of compiler moans

Revision 1.1.1.1  2015/06/12 10:44:32  jim
Initial import

Revision 1.7  2015/01/29 11:51:56  jim
modified comments

Revision 1.6  2015/01/09 12:13:15  jim
*** empty log message ***

Revision 1.5  2014/12/11 12:23:33  jim
new version

Revision 1.4  2014/04/09 09:09:51  jim
Detabbed

Revision 1.3  2014/03/26 15:50:37  jim
Modified for floating poing confidence maps

Revision 1.2  2013/11/21 09:38:14  jim
detabbed

Revision 1.1.1.1  2013-08-27 12:07:48  jim
Imported


*/
