/* $Id: fors_star.c,v 1.29 2010-09-14 07:49:30 cizzo Exp $
 *
 * This file is part of the FORS Library
 * Copyright (C) 2002-2010 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: cizzo $
 * $Date: 2010-09-14 07:49:30 $
 * $Revision: 1.29 $
 * $Name: not supported by cvs2svn $
 */

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

#include <fors_star.h>

#include <fors_utils.h>

#include <cpl.h>

#include <math.h>
#include <assert.h>

/**
 * @defgroup fors_star   star
 *
 * A data type used to represent stars on a CCD (position, magnitude, etc.)
 */

/**@{*/

/*-----------------------------------------------------------------------------
    Prototypes
 -----------------------------------------------------------------------------*/

static
double  _get_optional_table_value(  const cpl_table *tab,
                                    unsigned int    row,
                                    const char      *colname);

/*-----------------------------------------------------------------------------
    Private Implementation
 -----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief   Get a double value from a table
 * @param   row     Input row
 * @param   name    (Optional) Column name, can be NULL
 * @return  The value, 0.0 if no column name was specified or on error
 */
/*----------------------------------------------------------------------------*/
static
double  _get_optional_table_value(  const cpl_table *tab,
                                    unsigned int    row,
                                    const char      *colname)
{
    int null;
    if (colname != NULL && colname[0] != '\0')
    {
        double  d;
        d = cpl_table_get(          tab, colname, row, &null);
        assure(                     !cpl_error_get_code(),
                                    return d,
                                    "Missing column: %s",
                                        colname);
        return d;
            
    }
    else
        return 0.0;
}

/*-----------------------------------------------------------------------------
    Implementation
 -----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Constructor
 * @param  x           1st coordinate
 * @param  y           2nd coordinate
 * @param  fwhm        full width half maximum
 * @param  smajor      semi-major axis length
 * @param  sminor      semi-minor axis length
 * @param  orientation angle between semi-major axis and x-axis
 * @param  m           magnitude
 * @param  dm          magnitude error
 * @param  si          stellarity index
 * @return  newly allocated star
 */
/*----------------------------------------------------------------------------*/
fors_star *fors_star_new(double x, double y,
                         double fwhm,
                         double smajor, double sminor,
                         double orientation,
                         double m, double dm,
                         double si)
{
    assure( smajor >= sminor && sminor >= 0, return NULL,
            "Illegal semi major/minor axes: %g, %g",
            smajor, sminor );

    assure( 0 <= si && si <= 1, return NULL,
            "Stellarity index must be between 0 and 1, is %f",
            si);

    assure( fwhm >= 0, return NULL,
            "Star FWHM must be non-negative, is %f",
            fwhm);

    fors_star *s = cpl_malloc(sizeof(*s));

    s->pixel = fors_point_new(x, y);
    s->fwhm = fwhm;
    s->semi_major = smajor;
    s->semi_minor = sminor;
    s->stellarity_index = si;
    s->orientation = orientation;
    s->magnitude = m;
    s->dmagnitude = dm;
    s->magnitude_corr = 0;
    s->dmagnitude_corr = 0;
    s->id = NULL;
    s->weight = 0;

    return s;
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup fors_star_delete(&s)
/**
 * @brief   Create a star from a table WITHOUT checking
 * @param   tab         Input table
 * @param   row         Input row index
 * @param   x_col       (Optional) x column name
 * @param   y_col       (Optional) y column name
 * @param   fwhm_col    (Optional) fwhm column name
 * @param   smaj_col    (Optional) s_major column name
 * @param   smin_col    (Optional) s_minor column name
 * @param   theta_col   (Optional) orientation column name
 * @param   mag_col     (Optional) magnitude column name
 * @param   dmag_col    (Optional) magnitude error column name
 * @param   stlndx_col  (Optional) stellar index column name
 * @return  newly allocated star
 */
/*----------------------------------------------------------------------------*/
fors_star *fors_star_new_from_table(const cpl_table *tab,
                                    unsigned int    row,
                                    const char      *x_col,
                                    const char      *y_col,
                                    const char      *fwhm_col,
                                    const char      *smaj_col,
                                    const char      *smin_col,
                                    const char      *theta_col,
                                    const char      *mag_col,
                                    const char      *dmag_col,
                                    const char      *stlndx_col)
{
    int null;
    fors_star *s = cpl_malloc(sizeof(*s));

    s->pixel = fors_point_new(  _get_optional_table_value(tab, row, x_col),
                                _get_optional_table_value(tab, row, y_col));
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->fwhm =                   _get_optional_table_value(tab, row, fwhm_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->semi_major =             _get_optional_table_value(tab, row, smaj_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->semi_minor =             _get_optional_table_value(tab, row, smin_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->stellarity_index =       _get_optional_table_value(tab, row, stlndx_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->orientation =            _get_optional_table_value(tab, row, theta_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->magnitude =              _get_optional_table_value(tab, row, mag_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->dmagnitude =             _get_optional_table_value(tab, row, dmag_col);
    assure(!cpl_error_get_code(), return s, NULL);
    
    s->magnitude_corr = 0;
    s->dmagnitude_corr = 0;
    s->id = NULL;
    s->weight = 0;
    
    return s;
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Copy constructor
 * @param  star Input star
 * @return 1 if successful, 0 on error
 */
/*----------------------------------------------------------------------------*/
bool fors_star_check_values(        const fors_star *star)
{
    bool    success = 1;
    
    success &= (star != NULL);
    success &= (star->semi_major >= star->semi_minor);
    success &= (0.0 <= star->stellarity_index && star->stellarity_index <= 1.0);
    success &= (star->fwhm > 0.0);
    
    return success;
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Copy constructor
 * @param  star       to duplicate
 * @return  newly allocated star
 */
/*----------------------------------------------------------------------------*/
fors_star *fors_star_duplicate(const fors_star *star)
{
    fors_star *d;

    assure( star != NULL, return NULL, NULL );

    d = cpl_malloc(sizeof(*d));
    *d = *star;  /* Simple copy of all members, next do
                    deep copy of pointers */
    
    d->pixel = fors_point_duplicate(star->pixel);

    if (star->id != NULL) {
        d->id = fors_std_star_duplicate(star->id);
    }
    
    return d;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief  Delete object and set pointer to NULL
 * @param  star       to delete
 */
/*----------------------------------------------------------------------------*/
void fors_star_delete(fors_star **star)
{
    if (star && *star) {
        fors_point_delete(&(*star)->pixel);
        if ((*star)->id != NULL) {
            fors_std_star_delete_const(&((*star)->id));
        }
        cpl_free(*star); *star = NULL;
    }
    return;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief  Delete object and set pointer to NULL - but ignore the standard star
 * @param  star       to delete
 */
/*----------------------------------------------------------------------------*/
void fors_star_delete_but_standard(fors_star **star)
{
    if (star && *star) {
        fors_point_delete(&(*star)->pixel);
        cpl_free(*star); *star = NULL;
    }
    return;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief    Test for equality
 * @param    s         1st star
 * @param    t         2nd star
 * @return   true if and only if the two stars compare equal
 */
/*----------------------------------------------------------------------------*/
bool
fors_star_equal(const fors_star *s,
                const fors_star *t)
{
    assure( s != NULL && t != NULL, return true, NULL );
    
    return (fors_point_equal(s->pixel, t->pixel));
}

/*----------------------------------------------------------------------------*/
#undef clenaup
#define cleanup
/**
 * @brief  Compare star brightness
 * @param  s1       1st star
 * @param  s2       2nd star
 * @param  data     not used
 * @return true iff s1 is brighter than s2
 */
/*----------------------------------------------------------------------------*/
bool
fors_star_brighter_than(const fors_star *s1,
                        const fors_star *s2,
                        void *data)
{
    data = data;
    return (s1->magnitude < s2->magnitude);
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Get distance between stars
 * @param  s      1st star
 * @param  t      2nd star
 * @return  squared distance between stars
 */
/*----------------------------------------------------------------------------*/
double fors_star_distsq(const fors_star *s, const fors_star *t)
{
    assure( s != NULL, return 0, NULL );
    assure( t != NULL, return 0, NULL );

    return fors_point_distsq(s->pixel, t->pixel);
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Get star size 
 * @param  s      star
 * @param  data   not used
 * @return  average extension (one sigma)
 */
/*----------------------------------------------------------------------------*/
double fors_star_extension(const fors_star *s, void *data)
{
    assure( s != NULL, return -1, NULL );
    data = data;

    /* return sqrt(s->semi_major * s->semi_minor); */
    return s->fwhm / TWOSQRT2LN2;
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Get star stellarity
 * @param  s      star
 * @param  data   not used
 * @return  stellarity index
 */
/*----------------------------------------------------------------------------*/
double fors_star_stellarity(const fors_star *s, void *data)
{
    assure( s != NULL, return -1, NULL );
    data = data;

    return s->stellarity_index;
}

/*----------------------------------------------------------------------------*/
#undef cleanup
#define cleanup
/**
 * @brief  Get star ellipticity
 * @param  s      star
 * @param  data   not used
 * @return  geometric average of semi major/minor axes length
 */
/*----------------------------------------------------------------------------*/
double fors_star_ellipticity(const fors_star *s, void *data)
{
    assure( s != NULL, return -1, NULL );
    data = data;
    
    if (s->semi_major <= 0) return 1;
    else return 1 - (s->semi_minor / s->semi_major);
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Print object
 * @param   level      message level
 * @param   s          to print at the info level
 */
/*----------------------------------------------------------------------------*/
void fors_star_print(cpl_msg_severity level, const fors_star *s)
{
    if (s == NULL) {
        fors_msg(level, "[NULL]");
    }
    else {
        fors_msg(level, "at (%7.2f, %7.2f): m = %g +- %g (mc = %g +- %g), "
                 "shape: (%g, %g, %g)",
                 s->pixel->x, s->pixel->y,
                 s->magnitude, s->dmagnitude,
                 s->magnitude_corr, s->dmagnitude_corr,
                 s->orientation, s->semi_major, s->semi_minor);
    }

    return;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Print list of stars
 * @param   level      message level
 * @param   sl         list to print
 */
/*----------------------------------------------------------------------------*/
void
fors_star_print_list(cpl_msg_severity level, const fors_star_list *sl)
{
    if (sl == NULL) fors_msg(level, "Null list");
    else {
        const fors_star *s;
        
        for (s = fors_star_list_first_const(sl);
             s != NULL;
             s = fors_star_list_next_const(sl)) {
            fors_star_print(level, s);
        }
    }
    return;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Get position
 * @param   s        star
 * @param   data     not used
 * @return  x-position
 */
/*----------------------------------------------------------------------------*/
double
fors_star_get_x(const fors_star *s, void *data)
{
    assure( s != NULL, return -1, NULL );

    data = data;

    return s->pixel->x;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Get position
 * @param   s        star
 * @param   data     not used
 * @return  y-position
 */
/*----------------------------------------------------------------------------*/
double
fors_star_get_y(const fors_star *s, void *data)
{
    assure( s != NULL, return -1, NULL );

    data = data;

    return s->pixel->y;
}


/*----------------------------------------------------------------------------*/
/**
 * @brief   Get zeropoint
 * @param   s        star, must be identified
 * @param   data     not used
 * @return  zeropoint
 */
/*----------------------------------------------------------------------------*/
double
fors_star_get_zeropoint(const fors_star *s, void *data)
{
    assure( s     != NULL, return 0, NULL );
    assure( s->id != NULL, return 0, NULL );
    data = data;

    return (s->id->magnitude - s->magnitude_corr);
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Get zeropoint error
 * @param   s        star, must be identified
 * @param   data     not used
 * @return  zeropoint error
 */
/*----------------------------------------------------------------------------*/
double
fors_star_get_zeropoint_err(const fors_star *s, void *data)
{
    assure( s     != NULL, return 0, NULL );
    assure( s->id != NULL, return 0, NULL );
    data = data;
    
    return sqrt(s->dmagnitude_corr * s->dmagnitude_corr +
                s->id->dmagnitude * s->id->dmagnitude);
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Determine if star was identified
 * @param   s       star
 * @param   data    not used
 * @return  true iff star is identified
 */
/*----------------------------------------------------------------------------*/
bool
fors_star_is_identified(const fors_star *s, void *data)
{
    data = data;
    assure( s     != NULL, return 0, NULL );
    return (s->id != NULL && s->id->trusted);
}

#define LIST_DEFINE
#undef LIST_ELEM
#define LIST_ELEM fors_star
#include <list.h>

/**@}*/
