/** @file  pmTrend2D.c
 *
 *  @author EAM, IfA
 *
 *  @version $Revision: 1.1.2.3 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2007-09-20 19:21:13 $
 *
 *  Copyright 2004 Institute for Astronomy, University of Hawaii
 *
 */

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

# include <pslib.h>
# include "pmTrend2D.h"

static void pmTrend2DFree (pmTrend2D *trend) {

    if (trend == NULL) 
        return;

    psFree (trend->stats);
    psFree (trend->poly);
    psFree (trend->map);
    return;
}

pmTrend2D *pmTrend2DAlloc (pmTrend2DMode mode, psImage *image, int nXtrend, int nYtrend, psStats *stats)
{
    assert (image);
    assert (stats);

    pmTrend2D *trend = (pmTrend2D *) psAlloc(sizeof(pmTrend2D));
    psMemSetDeallocator(trend, (psFreeFunc) pmTrend2DFree);

    trend->map = NULL;
    trend->poly = NULL;
    trend->stats = psMemIncrRefCounter (stats);
    trend->mode = mode;
	
    switch (mode) {
      case PM_TREND_POLY_ORD:
	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXtrend, nYtrend);
	break;

      case PM_TREND_POLY_CHEB:
	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_CHEB, nXtrend, nYtrend);
	break;

      case PM_TREND_MAP: {
	  // binning defines the map scale relationship
	  psImageBinning *binning = psImageBinningAlloc();
	  binning->nXruff = nXtrend;
	  binning->nYruff = nYtrend;
	  binning->nXfine = image->numCols;
	  binning->nYfine = image->numRows;

	  trend->map = psImageMapAlloc (image, binning, stats);
	  psFree (binning);
	  break;
      }

      default:
	psAbort ("error");
    }
    return (trend);
}

pmTrend2D *pmTrend2DFieldAlloc (pmTrend2DMode mode, int nXfield, int nYfield, int nXtrend, int nYtrend, psStats *stats)
{
    pmTrend2D *trend = (pmTrend2D *) psAlloc(sizeof(pmTrend2D));
    psMemSetDeallocator(trend, (psFreeFunc) pmTrend2DFree);

    trend->map = NULL;
    trend->poly = NULL;
    trend->stats = psMemIncrRefCounter (stats);
    trend->mode = mode;
	
    switch (mode) {
      case PM_TREND_POLY_ORD:
	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_ORD, nXtrend, nYtrend);
	break;

      case PM_TREND_POLY_CHEB:
	trend->poly = psPolynomial2DAlloc (PS_POLYNOMIAL_CHEB, nXtrend, nYtrend);
	break;

      case PM_TREND_MAP: {
	  // binning defines the map scale relationship
	  psImageBinning *binning = psImageBinningAlloc();
	  binning->nXfine = nXfield;
	  binning->nYfine = nYfield;
	  binning->nXruff = nXtrend;
	  binning->nYruff = nYtrend;

	  trend->map = psImageMapAlloc (NULL, binning, stats);
	  psFree (binning);
	  break;
      }

      default:
	psAbort ("error");
    }
    return (trend);
}

bool pmTrend2DFit (pmTrend2D *trend, psVector *mask, psMaskType maskVal, psVector *x, psVector *y, psVector *f, psVector *df) {

    bool status;

    switch (trend->mode) {
      case PM_TREND_POLY_ORD:
      case PM_TREND_POLY_CHEB:
        status = psVectorClipFitPolynomial2D (trend->poly, trend->stats, mask, maskVal, f, df, x, y);
	// we can use the API here which adjusts the polynomial order based on the number
	// of points in the image, and potentially based on the fractional range of the
	// data?
	break;

      case PM_TREND_MAP:
	// XXX supply fraction from trend elements
	// XXX need to add the API which adjusts the scale
	status = psImageMapClipFit (trend->map, trend->stats, mask, maskVal, x, y, f, df);
	break;

      default:
	psAbort ("error");
    }
    return status;
}

double pmTrend2DEval (pmTrend2D *trend, float x, float y) {

    double result;

    if (!trend) return 0.0;

    switch (trend->mode) {
      case PM_TREND_POLY_ORD:
      case PM_TREND_POLY_CHEB:
	result = psPolynomial2DEval (trend->poly, x, y);
	break;

      case PM_TREND_MAP:
	result = psImageMapEval (trend->map, x, y);
	break;

      default:
	psAbort ("error");
    }
    return result;
}

psVector *pmTrend2DEvalVector (pmTrend2D *trend, psVector *x, psVector *y) {

    psVector *result;

    switch (trend->mode) {
      case PM_TREND_POLY_ORD:
      case PM_TREND_POLY_CHEB:
	result = psPolynomial2DEvalVector (trend->poly, x, y);
	break;

      case PM_TREND_MAP:
	result = psImageMapEvalVector (trend->map, x, y);
	break;

      default:
	psAbort ("error");
    }
    return result;
}
