/*
 * Simple implementation of the Sherpa gauss2D model as a user model
 */

#include <string.h>
#include <math.h>

#define FACTOR (-2.0 * log(4.0))

/* prototypes */
int ascfitusermodelinit_(void);
int ascfitusermodelsetparamvalues_(int* i, 
				   double* value, 
				   double* min, 
				   double*  max, 
				   char* name, 
				   char* descr, 
				   char* units, 
				   int* state, 
				   int* nameLen, 
				   int* descLen, 
				   int* unitsLen);  
int ascfitusermodelfunction_(double* parameterList,
			     int* numberOfDimensions,
			     int* numberOfData,
			     double* outValues,
			     double* x0,
			     double* x1,
			     double* x2,
			     double* x3,
			     double* x4,
			     double* x5,
			     double* x6);
void ascfitusermodelcleanup_(void);


/*
 * Initialization routine, returning 0 on success or 1 on failure.
 *
 * In this case we do nothing here. However, if your user model
 * needs values pre-calculated, or memory pre-allocated (e.g. to
 * read in values from a file), then this would be the place to
 * do this.
 */
int ascfitusermodelinit_(void)
{
  return 0;
}

/*
 * Parameter initialization routine.
 * Returns 0 for success, 1 for failure.
 *
 * We match the default values of the beta1D model.
 *
 * Input argument:
 *   i, the index of the parameter
 *
 * Output arguments:
 *   value: initial value of parameter
 *   min: initial lower limit of parameter value (when fitting)
 *   max: initial upper limit of parameter value (when fitting)
 *   name: name of parameter (max 32 chars)
 *   descr: description of parameter (max 32 chars)
 *   units: units of parameter (max 32 chars)
 *   state: state of parameter (0 means Frozen, 1 means Thawed)
 %
 * The string lengths nameLen, descLen, and unitsLen are used for the
 * fortran version of the interface, and can be ignored in the C interface
 *
 * This routine gets called once for each parameter (except the
 * parameter count, which is parameter 0) right after the allocation
 * of the parmeters in saofit. When you set the parameter count
 * in  the parameters get allocated, and this routine is
 * called.
 */
int ascfitusermodelsetparamvalues_(int* i, 
				   double* value, 
				   double* min, 
				   double*  max, 
				   char* name, 
				   char* descr, 
				   char* units, 
				   int* state, 
				   int* nameLen, 
				   int* descLen, 
				   int* unitsLen)
{
  switch (*i)
  {
  case 1:
    *value = 10.0;
    *min = 1.0e-120;
    *max = 1.0e+120;
    strcpy(name, "fwhm");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 1;
    break;

  case 2:
    *value = 0.0;
    *min = -1.0e+120;
    *max = 1.0e+120;
    strcpy(name, "xpos");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 1;
    break;

  case 3:
    *value = 0.0;
    *min = -1.0e+120;
    *max = 1.0e+120;
    strcpy(name, "ypos");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 1;
    break;

  case 4:
    *value = 0.0;
    *min = 0.0;
    *max = 0.999;
    strcpy(name, "ellip");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 0;
    break;

  case 5:
    *value = 0.0;
    *min = 0.0;
    *max = 6.2832;
    strcpy(name, "theta");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 0;
    break;

  case 6:
    *value = 1.0;
    *min = -1.0e120;
    *max = 1.0e120;
    strcpy(name, "ampl");
    strcpy(descr, "none");
    strcpy(units, " ");
    *state = 1;
    break;

  default:
    return 1;
  }

  return 0;
}
  
/*
 * This routine evaluates the model.
 * Returns 0 for success, 1 for failure.
 *
 * Input argument:
 *   parameterList, the 0th entry is the parameter count
 *   numberOfDimensions, the dimensionality of the dataspace
 *   numberOfData: size of data (length=numberOfDimensions)
 *   x0 .. x6, array of data bins defining the dataspace of the image
 *
 * Output arguments:
 *   outValues: array of size numberOfData, which should be filled by the
 *              routine
 *
 * This function gets called once for every iteration of the optimization
 * method through parameter space. This could be MANY times, so be
 * careful to make this routine as efficient as possible, and not to
 * allocate any memory if you can avoid it. If you do allocate, remember
 * to delete it before the return of the function.
 */
int ascfitusermodelfunction_(double* parameterList,
			     int* numberOfDimensions,
			     int* numberOfData,
			     double* outValues,
			     double* x0,
			     double* x1,
			     double* x2,
			     double* x3,
			     double* x4,
			     double* x5,
			     double* x6)
{
  double fwhm, xpos, ypos, ellip, theta, ampl;
  double dx, dy, xnew, ynew, radius, eterm;
  int i;

  /*
   * Check the number of parameters and that we are being
   * evaluated with a 2D dataset
   */
  if ((6 != parameterList[0]) || (2 != *numberOfDimensions))
    return 1;

  fwhm  = parameterList[1];
  xpos  = parameterList[2];
  ypos  = parameterList[3];
  ellip = parameterList[4];
  theta = parameterList[5];
  ampl  = parameterList[6];

  /*
   * See 'ahelp gauss2D'. No significant optimisation has
   * been attempted here.
   */
  eterm = 1.0 - ellip;
  for(i = 0; i < *numberOfData; i++)
  {
    dx = x0[i] - xpos;
    dy = x1[i] - ypos;

    if (ellip > 0.0)
    {
      xnew = dx * cos (theta) + dy * sin (theta);
      ynew = dy * cos (theta) - dx * sin (theta);
      radius = sqrt (xnew*xnew*eterm*eterm + ynew*ynew) / eterm; 
    }
    else
    {
      radius = sqrt (dx*dx + dy*dy);
    }

    outValues[i] = ampl * exp ((radius/fwhm) * (radius/fwhm) * FACTOR);
  }

  return 0;
}

/*
 * Cleanup routine. This is called when the userModel is deleted
 * by Sherpa. Any allocations made in the ascfitusermodelinit_
 * should be cleaned up here.
 *
 * In this example it does nothing.
 */
void ascfitusermodelcleanup_(void)
{
  return;
}

/* End */
