/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2018-Present Couchbase, Inc.
 *
 *   Use of this software is governed by the Business Source License included
 *   in the file licenses/BSL-Couchbase.txt.  As of the Change Date specified
 *   in that file, in accordance with the Business Source License, use of this
 *   software will be governed by the Apache License, Version 2.0, included in
 *   the file licenses/APL2.txt.
 */
#pragma once

#include <gsl/gsl-lite.hpp>
#include <platform/sized_buffer.h>
#include <array>
#include <optional>
#include <type_traits>

namespace cb::mcbp {

/**
 * Helper code for encode and decode of LEB128 values.
 * - mcbp encodes collection-ID as an unsigned LEB128
 * - see https://en.wikipedia.org/wiki/LEB128
 */

// Empty, non specialised version of the decoder class
template <class T, class Enable = void>
class unsigned_leb128 {};

/**
 * For encoding a unsigned T leb128, class constructs from a T value and
 * provides a const_byte_buffer for access to the encoded
 */
template <class T>
class unsigned_leb128<
        T,
        typename std::enable_if<std::is_unsigned<T>::value>::type> {
public:
    explicit unsigned_leb128(T in) {
        while (in > 0) {
            auto byte = gsl::narrow_cast<uint8_t>(in & 0x7full);
            in >>= 7;

            // In has more data?
            if (in > 0) {
                byte |= 0x80;
                encodedData[encodedSize - 1] = byte;
                // Increase the size
                encodedSize++;
            } else {
                encodedData[encodedSize - 1] = byte;
            }
        }
    }

    cb::const_byte_buffer get() const {
        return {encodedData.data(), encodedSize};
    }

    const uint8_t* begin() const {
        return encodedData.data();
    }

    const uint8_t* end() const {
        return encodedData.data() + encodedSize;
    }

    const uint8_t* data() const {
        return encodedData.data();
    }

    size_t size() const {
        return encodedSize;
    }

    constexpr static size_t getMaxSize() {
        return maxSize;
    }

    /**
     * decode returns the decoded T and a const_byte_buffer initialised with the
     * data following the leb128 data.
     *
     * @param buf buffer containing a leb128 encoded value (of size T). This can
     *            be a prefix on some other data, the decode will only process
     *            up to the maximum number of bytes permitted for the type T.
     *            E.g. uint32_t use 5 bytes maximum. buf.size must be >= 1
     *
     * @returns A std::pair where first is the decoded value and second is a
     *          buffer initialised with the data following the leb128 data. Note
     *          if the input buf was 100% only a leb128, the returned buffer
     *          will point outside of the input buf, but size will be 0.
     *
     * @throws std::invalid_argument if the input is not a valid leb128, this
     *         means decode processed 'getMaxSize' bytes without a stop byte.
     */
    static std::pair<T, cb::const_byte_buffer> decode(
            cb::const_byte_buffer buf);

    /**
     * decodeCanonical returns the decoded value of type T and a
     * const_byte_buffer initialised with the data following the leb128 data.
     *
     * This version does not throw an exception, but returns failure for two
     * reasons.
     *  - no-stop byte found
     *  - non-canonical encoding was used, e.g. 0x81.00 instead of 0x01
     *
     * The caller will have to inspect the input to determine the error.
     *
     * @param buf buffer containing a leb128 encoded value (of size T). This can
     *            be a prefix on some other data, the decode will only process
     *            up to the maximum number of bytes permitted for the type T.
     *            E.g. uint32_t use 5 bytes maximum. buf.size must be >= 1
     *
     * @returns On error a std::pair where the second buffer has a nullptr and
     *          zero size, first is set to 0. On success a std::pair where first
     *          is the decoded value and second is a buffer initialised with the
     *          data following the leb128 data. Note if the input buf was 100%
     *          only a leb128, the returned buffer will point outside of the
     *          input buf, but size will be 0.
     */
    static std::pair<T, cb::const_byte_buffer> decodeCanonical(
            cb::const_byte_buffer buf);

    /**
     * decodeNoThrow returns the decoded value of type T and a const_byte_buffer
     * initialised with the data following the leb128 data.
     *
     * This version does not throw an exception, but returns failure if no-stop
     * is byte found.
     *
     * @param buf buffer containing a leb128 encoded value (of size T). This can
     *            be a prefix on some other data, the decode will only process
     *            up to the maximum number of bytes permitted for the type T.
     *            E.g. uint32_t use 5 bytes maximum. buf.size must be >= 1
     *
     * @returns On error a std::pair where the second buffer has a nullptr and
     *          zero size, first is set to 0. On success a std::pair where first
     *          is the decoded value and second is a buffer initialised with the
     *          data following the leb128 data. Note if the input buf was 100%
     *          only a leb128, the returned buffer will point outside of the
     *          input buf, but size will be 0.
     */
    static std::pair<T, cb::const_byte_buffer> decodeNoThrow(
            cb::const_byte_buffer buf);

protected:
    struct NoThrow {};
    /**
     * decode returns the decoded value of type T and a const_byte_buffer
     * initialised with the data following the leb128 data.
     *
     * This is the protected inner method used by the public decode, does not
     * throw for bad input (allows public methods to decide error fate)
     */
    static std::pair<T, cb::const_byte_buffer> decode(cb::const_byte_buffer buf,
                                                      NoThrow);

    template <typename, typename>
    friend class unsigned_leb128;

    /**
     * Test that a decoded value was encoded in the canonical format.
     *
     * The test works by examining the length and comparing against a constant.
     * The constant is the maximum value that can be encoded as leb128 in
     * 'encodedLength - 1' bytes.
     *
     * For example if the encodedLength was 2 and the value was less than or
     * equal to 127, a non-canonical encoding was used, 127 and less can and
     * must be encoded in only 1 byte.
     *
     * So the test when encoded length is 2 is that the value is greater than
     * 127. If the encoded length is 3 the value must be greater than 16383 and
     * so on.
     *
     * @param value The integer that was decoded
     * @param encodedLength How many bytes the leb128 encoding used
     */
    static inline bool is_canonical(uint64_t value, size_t encodedLength);

private:
    // Larger T may need a larger array
    static_assert(sizeof(T) <= 8, "Class is only valid for uint 8/16/64");

    // value is large enough to store ~0 as leb128
    static constexpr size_t maxSize = sizeof(T) + (((sizeof(T) + 1) / 8) + 1);
    std::array<uint8_t, maxSize> encodedData{};
    uint8_t encodedSize{1};
};

// Generate the maximum value that can be encoded in nbytes
#define MAX_LEB128(nbytes) \
    ((0x7full << ((nbytes - 1) * 7)) | ((1ull << ((nbytes - 1) * 7)) - 1ull))

template <>
inline bool unsigned_leb128<uint8_t>::is_canonical(uint64_t value,
                                                   size_t encodedLength) {
    return (encodedLength == 2 && value > MAX_LEB128(1)) || encodedLength == 1;
}

template <>
inline bool unsigned_leb128<uint16_t>::is_canonical(uint64_t value,
                                                    size_t encodedLength) {
    if (unsigned_leb128<uint8_t>::is_canonical(value, encodedLength)) {
        return true;
    }
    return encodedLength == 3 && value > MAX_LEB128(2);
}

template <>
inline bool unsigned_leb128<uint32_t>::is_canonical(uint64_t value,
                                                    size_t encodedLength) {
    if (unsigned_leb128<uint16_t>::is_canonical(value, encodedLength)) {
        return true;
    }

    switch (encodedLength) {
    case 4:
        return value > MAX_LEB128(3);
    case 5:
        return value > MAX_LEB128(4);
    }

    return false;
}

template <>
inline bool unsigned_leb128<uint64_t>::is_canonical(uint64_t value,
                                                    size_t encodedLength) {
    // We first have to ask if this is non-canonical for the lower size, e.g.
    // u32. Each size asks the lower size first.
    if (unsigned_leb128<uint32_t>::is_canonical(value, encodedLength)) {
        return true;
    }

    switch (encodedLength) {
    case 6:
        return value > MAX_LEB128(5);
    case 7:
        return value > MAX_LEB128(6);
    case 8:
        return value > MAX_LEB128(7);
    case 9:
        return value > MAX_LEB128(8);
    case 10:
        return value > MAX_LEB128(9);
    }

    return false;
}

template <class T>
std::pair<T, cb::const_byte_buffer>
unsigned_leb128<T, typename std::enable_if<std::is_unsigned<T>::value>::type>::
        decode(cb::const_byte_buffer buf, NoThrow) {
    T rv = buf[0] & 0x7full;
    size_t end = 0;
    // Process up to the end of buf, or the max size for T, this ensures that
    // bad input, e.g. no stop-byte avoids invalid shifts (where shift just
    // keeps getting better). Primarily this gives us much better control over
    // invalid input, e.g. 20 bytes of 0x80 with a stop byte, would of
    // previously decoded to 0, but is really not valid input.
    size_t size =
            std::min(buf.size(), cb::mcbp::unsigned_leb128<T>::getMaxSize());
    if ((buf[0] & 0x80) == 0x80ull) {
        T shift = 7;
        // shift in the remaining data
        for (end = 1; end < size; end++) {
            rv |= (buf[end] & 0x7full) << shift;
            if ((buf[end] & 0x80ull) == 0) {
                break; // no more
            }
            shift += 7;
        }
        // We should of stopped for a stop byte, not the end of the buffer or
        // max encoding
        if (end == size) {
            return {0, cb::const_byte_buffer{}};
        }
    }
    // Return the decoded value and a buffer for any remaining data
    return {rv,
            cb::const_byte_buffer{buf.data() + end + 1,
                                  buf.size() - (end + 1)}};
}

template <class T>
std::pair<T, cb::const_byte_buffer>
unsigned_leb128<T, typename std::enable_if<std::is_unsigned<T>::value>::type>::
        decode(cb::const_byte_buffer buf) {
    if (buf.size() > 0) {
        auto rv = unsigned_leb128<T>::decode(buf, NoThrow{});
        if (rv.second.data()) {
            return rv;
        }
    }
    throw std::invalid_argument(
            "`unsigned_leb128::decode invalid leb128 of size:" +
            std::to_string(buf.size()));
}

template <class T>
std::pair<T, cb::const_byte_buffer>
unsigned_leb128<T, typename std::enable_if<std::is_unsigned<T>::value>::type>::
        decodeCanonical(cb::const_byte_buffer buf) {
    auto rv = unsigned_leb128<T>::decode(buf, NoThrow{});

    if (rv.second.data() &&
        !is_canonical(rv.first, size_t(rv.second.data() - buf.data()))) {
        return {0, cb::const_byte_buffer{}};
    }

    return rv;
}

template <class T>
std::pair<T, cb::const_byte_buffer>
unsigned_leb128<T, typename std::enable_if<std::is_unsigned<T>::value>::type>::
        decodeNoThrow(cb::const_byte_buffer buf) {
    return unsigned_leb128<T>::decode(buf, NoThrow{});
}

/**
 * @return a buffer to the data after the leb128 prefix
 */
template <class T>
typename std::enable_if<std::is_unsigned<T>::value, cb::const_byte_buffer>::type
skip_unsigned_leb128(cb::const_byte_buffer buf) {
    return unsigned_leb128<T>::decode(buf).second;
}

/// @return the index of the stop byte within buf
static inline std::optional<size_t> unsigned_leb128_get_stop_byte_index(
        cb::const_byte_buffer buf) {
    // If buf does not contain a stop-byte, invalid
    size_t stopByte = 0;
    for (auto c : buf) {
        if ((c & 0x80ull) == 0) {
            return stopByte;
        }
        stopByte++;
    }
    return {};
}

} // namespace cb::mcbp


/**
 * Subroutines used mostly for working on catalogs
 * in the tasks SEX2GOL and GOL2AF.
 *
 * Howard Bushouse, STScI, 04-Mar-2011, version 1.4
 * Changed FATAL error to WARN3 when get_valid_entries returns no valid
 * magnitudes for an object.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <gsl/gsl_vector.h>
#include <gsl/gsl_interp.h>

#include "aXe_utils.h"
#include "aXe_grism.h"
#include "spc_cfg.h"
#include "disp_conf.h"
#include "aper_conf.h"
#include "trace_conf.h"
#include "spc_CD.h"
#include "spc_wl_calib.h"
#include "aper_conf.h"
#include "specmodel_utils.h"
#include "model_utils.h"
#include "spc_fluxcube.h"
#include "spc_model.h"
#include "crossdisp_utils.h"
#include "spc_utils.h"
#include "spc_sex.h"

// define SQUARE
#define SQR(x) ((x)*(x))

/**
 *
 *  Function: create_SexObject
 *  Allocates and creates a SExtractor object from
 *  a line of a SExtractor catalog.
 *
 *  Parameters:
 *  @param  actinfo  - the header structure with the column names
 *  @param  line     - the line to be transformed into a SexObject
 *  @param  waves    - vector with the walelengths of the magnitude columns
 *  @param  cnums    - vector with the column numbers of the magnitude columns
 *  @param  magcol   - column number of the magnitude column selected
 *                     for mag_auto
 *  @param  fullinfo - deecides whether a column is optional or not
 *
 *  Returns:
 *  @return o        - the new created SexObject
 */
SexObject *
create_SexObject(const colinfo *actinfo, char *line, const gsl_vector * waves,
                 const gsl_vector *  cnums, const px_point backwin_cols,
                 const px_point modinfo_cols, const int magcol, const int fullinfo)
{
  gsl_vector *v;
  gsl_vector *mags;
  gsl_vector *wavs;
  SexObject  *o;
  int         i=0;
  // transform the data in the line to a vector
  lv1ws (line);
  v = string_to_gsl_array (line);

  // allocate space for the SexObject
  o = (SexObject *) malloc (sizeof (SexObject));
  if (o == NULL)
    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                 "create_SexObject: Could not allocate memory.");

  // succesively fill the data into the SexObject.
  // for 'fullinfo=1' missing data results in an error.
  // otherwise NaN is stored into the SexObject
  o->number = (int) get_col_value (actinfo, "NUMBER", v, 1);

  o->xy_image.x     = get_col_value (actinfo, "X_IMAGE", v,fullinfo);
  o->xy_image.y     = get_col_value (actinfo, "Y_IMAGE", v,fullinfo);

  o->xy_world.ra    = get_col_value (actinfo, "X_WORLD", v,1);
  o->xy_world.dec   = get_col_value (actinfo, "Y_WORLD", v,1);

  o->el_image.a     = get_col_value (actinfo, "A_IMAGE", v,fullinfo);
  o->el_image.b     = get_col_value (actinfo, "B_IMAGE", v,fullinfo);
  o->el_image.theta = get_col_value (actinfo, "THETA_IMAGE", v,fullinfo);

  o->el_world.a     = get_col_value (actinfo, "A_WORLD", v,1);
  o->el_world.b     = get_col_value (actinfo, "B_WORLD", v,1);

  // Some clarification is needed for the next two lines.
  // to have get_col_value and get_col_value2 is not
  // satisfying!! PROBLEM
  o->el_world.theta = get_col_value2 (actinfo, "THETA_WORLD", v,0);
  if (o->el_world.theta <-900000){
    //  o->el_world.theta = get_col_value (actinfo, "THETA_WORLD", v,0);
    //  if (isnan(o->el_world.theta)){
    o->el_world.theta = get_col_value (actinfo, "THETA_SKY", v,1);
  }

  if (backwin_cols.x !=-1)
    {
      o->backwindow.x  = get_col_value (actinfo, "BACKWINDOW", v,1);
      o->backwindow.y  = -1;
      // old code for FORS2-MXU
      // o->backwindow.y     = get_col_value (actinfo, "BACKWIN_LOW", v,1);

    }
  else
    {
      o->backwindow.x     = -1;
      o->backwindow.y     = -1;
    }

  // transfer the index for the model spectrum
  if (modinfo_cols.x > -1)
    o->modspec = (int)get_col_value(actinfo, "MODSPEC", v,1);
  else
    o->modspec = -1;

  // transfer the index value for the object shape
  if (modinfo_cols.y > -1)
    o->modimage = (int)get_col_value(actinfo, "MODIMAGE", v,1);
  else
    o->modimage = -1;

  // check whether the MAG_AUTO column exists
  if ((int)waves->size == 1 && (int)gsl_vector_get (waves, 0) == 0)
    {

      // fill the MAG_AUTO value
      // put the magnitude and wavelength vectors to NULL
      o->magnitudes = NULL;
      o->lambdas    = NULL;
      o->mag_auto   = get_col_value (actinfo, "MAG_AUTO", v, 1);
    }
  else
    {
      // create the vectors for the magnitude values
      mags = gsl_vector_alloc ((int)waves->size);
      wavs = gsl_vector_alloc ((int)waves->size);

      // fill the magnitude values into the vectors and store them in
      // the SexObject.
      for (i=0; i < (int)waves->size; i++){
        gsl_vector_set(mags, i, gsl_vector_get (v, (int)gsl_vector_get (cnums, i)-1));
        gsl_vector_set(wavs, i, gsl_vector_get (waves, i));
      }

      // fill the MAG_AUTO value
      o->magnitudes = mags;
      o->lambdas    = wavs;
      o->mag_auto   = gsl_vector_get (v, magcol-1);
    }

  return o;
}

/**
 *  Function: SexObject_fprintf
 *  Function to print the attributes of a SexObbject
 *  to an output stream.
 *
 *  Paramters:
 *  @param output - an ouput file or stream
 *  @param o      - a pointer to a SexObject
 */
void
SexObject_fprintf (FILE * output, SexObject * o)
{
     fprintf (output, "NUMBER: %d\n", o->number);

     fprintf (output, "X_IMAGE: %e\n", o->xy_image.x);
     fprintf (output, "Y_IMAGE: %e\n", o->xy_image.y);

     fprintf (output, "X_WORLD: %e\n", o->xy_world.ra);
     fprintf (output, "Y_WORLD: %e\n", o->xy_world.dec);

     fprintf (output, "A_IMAGE: %e\n", o->el_image.a);
     fprintf (output, "B_IMAGE: %e\n", o->el_image.b);
     fprintf (output, "THETA_IMAGE: %e\n", o->el_image.theta);

     fprintf (output, "A_WORLD: %e\n", o->el_world.a);
     fprintf (output, "B_WORLD: %e\n", o->el_world.b);
     fprintf (output, "THETA_WORLD: %e\n", o->el_world.theta);

     fprintf (output, "MAG_AUTO: %e\n", o->mag_auto);

}

/**
 *  Function: sobs_to_vout
 *  The function transforms a SexObject into
 *  a vector.
 *
 *  Parameters:
 *  @param  sobs - the SexObject to be transformed
 *
 *  Returns:
 *  @return vout - the vector with the Seobject data
 */
gsl_vector *
sobs_to_vout(const SexObject *sobs)
{

  int nentries, i;
  int count=0;

  // determine the size of the vector
  if (sobs->magnitudes){
    nentries = 11+sobs->magnitudes->size;
  }
  else{
    nentries = 12;
  }

  if(sobs->backwindow.x != -1)
    nentries = nentries+1;
    // old FORS2-MXU code:
    //nentries = nentries+2;

  if(sobs->modspec != -1)
    nentries++;
  if(sobs->modimage != -1)
    nentries++;

  // allocate space for the vector
  gsl_vector *vout = gsl_vector_alloc (nentries);

  // fill the vector
  gsl_vector_set (vout, count++, sobs->number);
  gsl_vector_set (vout, count++, sobs->xy_world.ra);
  gsl_vector_set (vout, count++, sobs->xy_world.dec);
  gsl_vector_set (vout, count++, sobs->el_world.a);
  gsl_vector_set (vout, count++, sobs->el_world.b);
  gsl_vector_set (vout, count++, sobs->el_world.theta);
  gsl_vector_set (vout, count++, sobs->xy_image.x);
  gsl_vector_set (vout, count++, sobs->xy_image.y);
  gsl_vector_set (vout, count++, sobs->el_image.a);
  gsl_vector_set (vout, count++, sobs->el_image.b);
  gsl_vector_set (vout, count++, sobs->el_image.theta);

  if(sobs->backwindow.x != -1)
    {
      gsl_vector_set (vout, count++, sobs->backwindow.x);
      // old code for FORS2-MXU
      //gsl_vector_set (vout, count++, sobs->backwindow.y);
    }

  if(sobs->modspec != -1)
      gsl_vector_set (vout, count++, sobs->modspec);

  if(sobs->modimage != -1)
      gsl_vector_set (vout, count++, sobs->modimage);

  // see whether there is MAG_AUTO
  if (sobs->magnitudes){
    // fill in the magnitude values
    for (i=0; i < (int)sobs->magnitudes->size; i++)
      gsl_vector_set (vout, count++, gsl_vector_get(sobs->magnitudes, i));
  }
  else{
    // fill in the MAG_AUTO value
    gsl_vector_set (vout, count++, sobs->mag_auto);
  }

  // return the vector
  return vout;
}

/**
 *  Function: catalog_to_wcs
 *  This function reads a sextractor catalog and using an input and an output
 *  CD matrix, converts the world ra and dec coordinates to the output CD
 *  matrix's image pixel coordinates. Non existing (i.e. NaN) world RA and
 *  DEC values are generated using
 *  the pixel coordinates and the input CD matrix
 *
 *  Parameters:
 *  @param - infile a pointer to a char array containing the name of the
 *         input catalog
 *  @param - outfile a pointer to a char array containing the name of the
 *         output catalog
 *  @param - from_wcs a pointer to an exisiting WoldCoor structure containing
 *         the input catalog CD matrix
 *  @param - to_wcs a pointer to an exisiting WoldCoor structure containing
 *         the output catalog CD matrix
 *  @param - overwrite_wcs if set to 1 then input wold coordinate are
 *         recomputed first using the from_wcs and the x,y image values
 *  @param - overwrite_img if set to 1 then the x,y coordinates of the input
 *         image are first computed using the from_wcs and the
 *          world ra and dec info
 */
void
catalog_to_wcs (char grismfile[], int hdunum, char infile[], char outfile[],
                struct WorldCoor *from_wcs, struct WorldCoor *to_wcs,
                int distortion, int overwrite_wcs, int overwrite_img)
{
  FILE *fin, *fout;
  gsl_vector *v;
  gsl_vector *v_out;
  gsl_vector *waves;
  gsl_vector *cnums;
  gsl_matrix *coeffs=NULL;
  char Buffer[CATBUFFERSIZE];
  char line[CATBUFFERSIZE], str[CATBUFFERSIZE];
  int i, hasmags=0, magcencol = 0;
  SexObject *o;
  colinfo * actcatinfo;
  px_point    pixmax;
  px_point    backwin_cols;
  px_point    modinfo_cols;

  actcatinfo = get_sex_col_descr (infile);
  hasmags = has_magnitudes(actcatinfo);
  if (!hasmags)
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "No magnitudes in file %s", infile);
    }
  else{
    waves = gsl_vector_alloc (hasmags);
    cnums = gsl_vector_alloc (hasmags);
    hasmags = get_magcols(actcatinfo, waves, cnums);
    magcencol = get_magauto_col(waves, cnums, 800.0);
  }

  backwin_cols = has_backwindow(actcatinfo);
  modinfo_cols = has_modelinfo(actcatinfo);

  if (!(fin = fopen (infile, "r")))
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Cannot open catalog file %s", infile);
    }
  if (!(fout = fopen (outfile, "w")))
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Cannot open catalog file %s", outfile);
    }

  if (check_worldcoo_input(actcatinfo, 0))
    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                 "Catalogue %s does not have all WCO columns\n", infile);

  if (check_imagecoo_input(actcatinfo))
    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                 "Catalogue %s does not have all image coo columns\n", infile);

  make_GOL_header(fout, actcatinfo, waves, cnums, backwin_cols, modinfo_cols);

  if (distortion)
    coeffs = get_crossdisp_matrix(grismfile, hdunum);

  pixmax = get_npixels (grismfile, hdunum);

  while (fgets (line, CATBUFFERSIZE, fin))
    {
      if (!(line_is_valid (actcatinfo, line)))
        continue;
       if (line[0] == ';')
         continue;

      /* Create a vector containing the information */
      lv1ws (line);
      v = string_to_gsl_array (line);

      //      o = create_SexObject (sex_col_desc, line);
      o = create_SexObject (actcatinfo, line, waves, cnums, backwin_cols, modinfo_cols, magcencol,  1);

      if ( (from_wcs!=NULL)&&(to_wcs!=NULL) )
        {
          //      if (distortion){
          //        fprintf(stdout, "(%f,%f) &&-->&& ", o->xy_image.x, o->xy_image.y);
          //        o->xy_image = undistort_point(coeffs, pixmax, o->xy_image);
          //      }
          fill_missing_WCS_coordinates (o, from_wcs, overwrite_wcs);
          fill_missing_image_coordinates (o, from_wcs, overwrite_img);
          compute_new_image_coordinates (o, to_wcs);
          if (distortion){
            o->xy_image = distort_point(coeffs, pixmax, o->xy_image);
            //      fprintf(stdout, "(%f,%f)\n\n", o->xy_image.x, o->xy_image.y);
          }
        }

      v_out = sobs_to_vout(o);

      sprintf (Buffer, "%8.5g ", gsl_vector_get (v_out, 0));
      for (i = 1; i < (int)v_out->size; i++)
        {
          sprintf (str, " %8.5g ", gsl_vector_get (v_out, i));
          strcat (Buffer, str);
        }
      strcat (Buffer, "\n");

      fputs (Buffer, fout);

      gsl_vector_free (v_out);
      gsl_vector_free (v);
  }

  if (coeffs)
    gsl_matrix_free(coeffs);

  fclose (fin);
  fclose (fout);
}

/**
 * Function:catalog_to_wcs_nodim
 * This function reads a sextractor catalog and using an input and an output
 * CD matrix, converts the world ra and dec coordinates to the output
 * CD matrix's image pixel coordinates. Non existing (i.e. NaN) world RA
 * and DEC values are generated using the pixel coordinates and the input
 * CD matrix
 *
 * Parameters:
 * @param infile    - a pointer to a char array containing the name of the
 *                    input catalog
 * @param outfile   - a pointer to a char array containing the name of the
 *                    output catalog
 * @param from_wcs  - a pointer to an exisiting WoldCoor structure containing
 *                    the input catalog CD matrix
 * @param to_wcs    - a pointer to an exisiting WoldCoor structure
 *                    containing the output catalog CD matrix
 * @param overwrite_wcs - if set to 1 then input wold coordinate are
 *                        recomputed first using the from_wcs and the
 *                        x,y image values
 * @param overwrite_img - if set to 1 then the x,y coordinates of the
 *                        input image are first computed using the
 *                        from_wcs and the world ra and dec info
 */
void
catalog_to_wcs_nodim (char infile[], char outfile[],
                      struct WorldCoor *grism_wcs, int overwrite_wcs,
                      int overwrite_img)
{
  FILE *fin, *fout;
  gsl_vector *v;
  gsl_vector *v_out;
  gsl_vector *waves;
  gsl_vector *cnums;
  char Buffer[CATBUFFERSIZE];
  char line[CATBUFFERSIZE], str[CATBUFFERSIZE];
  int i;
  SexObject *o;
  int compute_imcoos=0;
  int th_sky=0;
  int checksum, hasmags=0, magcencol=0;
  colinfo * actcatinfo;
  px_point    backwin_cols;
  px_point    modinfo_cols;

  actcatinfo = get_sex_col_descr (infile);
  hasmags = has_magnitudes(actcatinfo);
  if (!hasmags)
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "No magnitudes in file %s", infile);
    }
  else{
    waves = gsl_vector_alloc (hasmags);
    cnums = gsl_vector_alloc (hasmags);
    hasmags = get_magcols(actcatinfo, waves, cnums);
    magcencol = get_magauto_col(waves, cnums, 800.0);
  }

  backwin_cols = has_backwindow(actcatinfo);
  modinfo_cols = has_modelinfo(actcatinfo);

  if (!(fin = fopen (infile, "r")))
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Cannot open catalog file %s", infile);
    }
  if (!(fout = fopen (outfile, "w")))
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Cannot open catalog file %s", outfile);
    }

  //  checksum = check_worldcoo_input(sex_col_desc);
  th_sky=1;
  checksum = check_worldcoo_input(actcatinfo, 1);
  if (checksum)
    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                 "Catalogue %s does not have all necessary columns\n", infile);

  //  if (check_imagecoo_input(sex_col_desc))
  if (check_imagecoo_input(actcatinfo))
    compute_imcoos=1;

  make_GOL_header(fout, actcatinfo, waves, cnums, backwin_cols, modinfo_cols);

  while (fgets (line, CATBUFFERSIZE, fin))
    {

      /* If line is not a valid catalog entry, just continue */
      if (!(line_is_valid (actcatinfo, line)))
        continue;

      /* if line starts with ";", just continue */
      if (line[0] == ';')
        continue;

      /* Create a vector containing the information */
      lv1ws (line);
      v = string_to_gsl_array (line);

      o = create_SexObject (actcatinfo, line, waves, cnums, backwin_cols, modinfo_cols, magcencol,  0);

      if (compute_imcoos)
        compute_new_image_sexobject (o, grism_wcs, th_sky);
      v_out = sobs_to_vout(o);

      sprintf (Buffer, "%8.5g ", gsl_vector_get (v_out, 0));
      for (i = 1; i < (int)v_out->size; i++)
        {
          sprintf (str, " %8.5g ", gsl_vector_get (v_out, i));
          strcat (Buffer, str);
        }
      strcat (Buffer, "\n");

      fputs (Buffer, fout);

      gsl_vector_free (v_out);
      gsl_vector_free (v);
    }

  fclose (fin);
  fclose (fout);

  free (actcatinfo);

}

/**
 * Function: SexMags_to_beamFlux
 * Fill an beam structure with the flux and shape information information
 * contained in a SexObject structure.
 *
 * Parameters:
 * @param sobj     - pointer to a  SexObject
 * @param actbeam  - the beam structure to be filled
 *
 * Returns:
 * @return -
 */
void
SexMags_to_beamFlux(SexObject * sobj, beam *actbeam)
  {
    int nvalid=0;
    int jj=0;
    int j=0;

    double fval=0.0;

    gsl_vector *flux;

    // set 'default' shapes
    actbeam->awidth  = -1.0;
    actbeam->bwidth  = -1.0;
    actbeam->aorient = -1.0;

    // set a 'default' flux
    actbeam->flux = NULL;

    /* if flux/wavelength information exists */
    if (sobj->magnitudes)
      {

        // get the number of valid entries in 'sobj->magnitudes'
        nvalid = get_valid_entries(sobj->magnitudes);
        if (!nvalid)
          aXe_message (aXe_M_WARN3, __FILE__, __LINE__,
                       "No valid magnitude for object %i !\n", sobj->number);

        // allocate the flux vector
        flux = gsl_vector_alloc (2*(nvalid));

        // initialize a counter
        jj=0;

        //  go over all magnidute values
        for (j=0; j < (int)sobj->magnitudes->size; j++)
          {
            // check whether the current entry is valid
            if (is_valid_entry(gsl_vector_get(sobj->magnitudes, j)))
              {
                // get the current entry, already converted to flux
                fval = get_flambda_from_magab(gsl_vector_get(sobj->magnitudes, j), gsl_vector_get(sobj->lambdas, j));

                // set the wavelength value and the flux value
                gsl_vector_set(flux, 2*jj, gsl_vector_get(sobj->lambdas, j));
                gsl_vector_set(flux, 2*jj+1, fval);

                // enhance the counter
                jj++;
              }
          }

        // transfer the flux values
        // to the beam
        actbeam->flux = flux;

        // transfer the shape values
        // to the beam
        actbeam->awidth  = sobj->el_image.a;
        actbeam->bwidth  = sobj->el_image.b;
        actbeam->aorient = sobj->el_image.theta;
      }
  }

/**
 * Function: SexObject_to_slitgeom
 * Fill a beam structure with the optimized geometry information.
 * The quantities are computed from the object shape and
 * the trace angle.
 *
 * Parameters:
 * @param sobj        - pointer to a  SexObject
 * @param trace_angle - pointer to a  SexObject
 * @param actbeam     - the beam structure to be filled
 *
 * Returns:
 * @return -
 */
void
SexObject_to_slitgeom(const aperture_conf *conf, const SexObject * sobj,
                      const double trace_angle, beam *actbeam)
  {
    double theta=0.0;
    double orient=0.0;
    double tmp_angle, cos_tmp_angle;
    double A11, A12, A22;

    d_point obj_size;

    // convert the SExtractor angle to rad in the right quadrant
    orient = (180.0 + sobj->el_image.theta) / 180.0 * M_PI;
    while (orient > M_PI)
      orient = orient - M_PI;

    // compute angle between object
    // orientation and trace
    theta = orient - trace_angle;

    // check the object size, possibly setting
    // it to the size of point-like object
    obj_size = check_object_size(conf, sobj, actbeam->ID);

    /*
    // determine the three matrix elements
    A11 = SQR(cos(theta) / sobj->el_image.a) + SQR(sin(theta) / sobj->el_image.b);
    A12 = cos(theta) * sin(theta) * (1.0/(SQR(sobj->el_image.a)) - 1.0/SQR(sobj->el_image.b));
    A22 = SQR(sin(theta) / sobj->el_image.a) + SQR(cos(theta) / sobj->el_image.b);

    // compute a temporary angle
    // and its cosine
    tmp_angle     = atan(A12 / A11);
    cos_tmp_angle = cos(tmp_angle);

    // compute the slit length
    if (cos_tmp_angle > 0.01)
      actbeam->slitgeom[0] = sqrt(A11)*sobj->el_image.a*sobj->el_image.b / cos_tmp_angle;
    else
      actbeam->slitgeom[0] = sqrt(A11)*sobj->el_image.a*sobj->el_image.b / 0.01;

    // compute the slit orientation
    actbeam->slitgeom[1] = tmp_angle + trace_angle + M_PI_2;

    // compute the slit length
    actbeam->slitgeom[2] = 1.0 / sqrt(A11);

    // compute a modified B_IMAGE value, defined to keep the object area constant
    actbeam->slitgeom[3] = sobj->el_image.a*sobj->el_image.b / actbeam->slitgeom[0];
    */

    // determine the three matrix elements
    A11 = SQR(cos(theta) / obj_size.x) + SQR(sin(theta) / obj_size.y);
    A12 = cos(theta) * sin(theta) * (1.0/(SQR(obj_size.x)) - 1.0/SQR(obj_size.y));
    A22 = SQR(sin(theta) / obj_size.x) + SQR(cos(theta) / obj_size.y);

    // compute a temporary angle
    // and its cosine
    tmp_angle     = atan(A12 / A11);
    cos_tmp_angle = cos(tmp_angle);

    // compute the slit length
    if (cos_tmp_angle > 0.01)
      actbeam->slitgeom[0] = sqrt(A11)*obj_size.x*obj_size.y / cos_tmp_angle;
    else
      actbeam->slitgeom[0] = sqrt(A11)*obj_size.x*obj_size.y / 0.01;

    // compute the slit orientation
    actbeam->slitgeom[1] = tmp_angle + trace_angle + M_PI_2;

    // compute the slit width
    actbeam->slitgeom[2] = 1.0 / sqrt(A11);

    // compute a modified B_IMAGE value, defined to keep the object area constant
    actbeam->slitgeom[3] = obj_size.x*obj_size.y / actbeam->slitgeom[0];
  }

/**
 * Function: fill_corner_ignore
 * The method computes and fills the beam corner information
 * and the the ignore flag into a beam structure.
 *
 * Parameters:
 * @param sobj     - pointer to a  SexObject
 * @param obs      - a pointer to the data array containing the image
 * @param conf     - pointer to the configuration structure
 * @param beamID   - the beam ID
 * @param dmag     - number of magnitudes to add to the magnitudes cutoffs
 * @param actbeam  - the beam structure to be filled
 *
 * Returns:
 * @return -
 */
void
fill_corner_ignore(SexObject * sobj, observation * const obs, aperture_conf *conf,
                   int beamID, float dmag, beam *actbeam)
  {
    double mmag_extract;
    double mmag_mark;

    // get the extraction and the mark margnitudes
    mmag_extract = conf->beam[beamID].mmag_extract + dmag;
    mmag_mark    = conf->beam[beamID].mmag_mark    + dmag;

    // make a default for the
    // ignore flag
    actbeam->ignore = 0;

    /* magnitude cut */
    // PROBLEM: there is a logical flaw inside
    //          whhat happend when (mag > mag_mark) && (mag <  mmag_extract) ????
    //          of course this has only relevance when mag_mark < mmag_extract
    //      if ((sobj->mag_auto <= mmag_mark)&&(sobj->mag_auto <= mmag_extract))
    // That's now an easy fix, but makes sense..
    if (sobj->mag_auto <= mmag_extract)
        actbeam->ignore = 0; // this object will be extracted

    if ((sobj->mag_auto <= mmag_mark)&&(sobj->mag_auto > mmag_extract))
        actbeam->ignore = 2; // this object will be not be extracted

    if ((sobj->mag_auto > mmag_mark)&&(sobj->mag_auto > mmag_extract))
        actbeam->ignore = 1; // this object will be ignored

    // fill the boundary box values;
    // returns if the beams is completely out of the image
    if (!fill_object_bbox (obs, actbeam, 2, conf->beam[beamID].offset.dx0,
                           conf->beam[beamID].offset.dx1))
      actbeam->ignore = 1;
  }

/**
 * Function: set_extraction_parameters
 * The function computes and fills the extraction width and the
 * extraction orientation into a beam structure. Depending on the
 * input parameters and the object shape, different methods
 * are deployed.
 *
 * Parameters:
 * @param sobj     - pointer to a  SexObject
 * @param bck_mode - pointer for background mode
 * @param mfwhm    - the fwhm multiplicator constant to apply to
 *                   determine the width of the aperture box for the object.
 * @param dmag     - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set then this task tries to optimize
 *                        the orientation of the
 *                        extraction slit - Should in general be left at 1 so that strange
 *                        geomety is avoided. If set to 2 the extraction is
 *                        forced to be vertical (90 deg.)
 * @param trace_angle - the trace angle
 * @param actbeam     - the beam structure to be filled
 *
 * Returns:
 * @return -
 */
void
set_extraction_parameters(SexObject * sobj, int bck_mode, float mfwhm,
                          int auto_reorient, double trace_angle, beam *actbeam)
  {
    double orient;
    double theta;
    double theta_deg;
    double trace_dist;
    double dya;
    double dyb;

    //int iturn=0;

    // convert the orientation to rad;
    // turn into the right quadrant
    orient = (180.0 + sobj->el_image.theta) / 180.0 * M_PI;
    while (orient > M_PI)
      orient = orient - M_PI;

    // compute the relative between the trace angle
    // and object orientation
    theta = orient - trace_angle;

    // convert theta to deg;
    // convert it into the range:
    // 0.0 < theta_deg < 180.0
    theta_deg = theta / M_PI * 180.;
    while (theta_deg < 0.0)
      theta_deg += 180.0;
    while (theta_deg > 180.0)
      theta_deg -= 180.0;

    // that's the 'normal', slanted extaction
    if (auto_reorient == 0)
      {
        // take the major half axis value * mfwhm
        // as width and its orientation
        // as extraction direction
        actbeam->width  = sobj->el_image.a * mfwhm;
        actbeam->orient = orient;

        // give a warning for small angles
        // between the orientation direction and
        // the trace angle.
        if (fabs(theta_deg )< MIN_DIFFANGLE || fabs(theta_deg-180.0) < MIN_DIFFANGLE)
          aXe_message (aXe_M_WARN4, __FILE__, __LINE__,
                       "aXe_GOL2AF: Object ID: %i : The angle between the extraction orientation and "
                       "the trace is less than %5.2f degrees. You may get severe problems"
                       " down to core dumps in the 1D extraction later on!\n", sobj->number, MIN_DIFFANGLE);
      }
    // thats the slanted, adjusted extraction
    else if (auto_reorient == 1)
      {
        // use the optimized parameters
        actbeam->width  = actbeam->slitgeom[0] * mfwhm;
        actbeam->orient = actbeam->slitgeom[1];
      }
    // thats the perpendicular extraction
    else if(auto_reorient == 2)
      {
        // extraction angle is prependicular to trace
        actbeam->orient = trace_angle + M_PI_2;
        if (mfwhm < 0.0)
          {
            // take the mfwhm value as width;
            actbeam->width  = -1.0 * mfwhm;
          }
        else
          {
            // compute the projections of the major and minor
            // half axis to the extraction direction
            dya = fabs (sobj->el_image.a * sin (theta));
            dyb = fabs (sobj->el_image.b * sin (M_PI_2 - theta));

            // use the larger for extraction width
            actbeam->width = mfwhm*MAX(dya,dyb);
          }
      }

    // this piece of code was specifically added
    // for NICMOS HLA, inorder to nelarge the background
    // area around bright, point-like objects.
    if (sobj->backwindow.x != -1 && bck_mode)
      {
        // compute the projected distance to the trace
        trace_dist = sin(actbeam->orient - trace_angle) * actbeam->width;

        // check whether the distance is too small
        if (trace_dist < sobj->backwindow.x)
          // elongate the width such that the projected distance
          // equals the minimum value given in the SExobject
          actbeam->width = sobj->backwindow.x / sin(actbeam->orient - trace_angle);
      }
  }

d_point
check_object_size(const aperture_conf *conf, const SexObject *sobj, const int beamID)
  {
    double obj_size = 0.0;

    d_point ret;

    // set the input values
    // as default
    ret.x = sobj->el_image.a;
    ret.y = sobj->el_image.b;

    // check whether a minimum
    // size is defined
    if (conf->pobjsize < 0.0)
      // return the value
      return ret;

    // compute the object size
    obj_size = sobj->el_image.a * sobj->el_image.b;

    // check whether the object is point-like
    if (obj_size < SQR(conf->pobjsize))
      {
        // transfer the point-like
        // sizes to the return
        ret.x = conf->pobjsize;
        ret.y = conf->pobjsize;
      }

    // return the resulting
    // object size
    return ret;
  }

/**
 * Function: SexObject_to_beam
 * Fill an beam structure with the information contained in a SexObject
 * structure plus further information derived via the confiugration file.
 *
 * Parameters:
 * @param sobj     - pointer to a  SexObject
 * @param obs      - a pointer to the data array containing the image
 * @param conf     - pointer to the configuration structure
 * @param conffile - the name of the aperture configuration file
 * @param mfwhm    - the fwhm multiplicator constant to apply to
 *                   determine the width of the aperture box for the object.
 * @param dmag     - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set then this task tries to optimize
 *                        the orientation of the
 *                        extraction slit - Should in general be left at 1 so that strange
 *                        geomety is avoided. If set to 2 the extraction is
 *                        forced to be vertical (90 deg.)
 * @param bck_mode - pointer for background mode
 * @param beamID   - the beam ID
 * @param actbeam  - the beam structure to be filled
 *
 * Returns:
 * @return -
 */
void
SexObject_to_beam(SexObject * sobj, observation * const obs, aperture_conf *conf,
                  char conffile[], float mfwhm, float dmag, int auto_reorient,
                  int bck_mode, int beamID, beam *actbeam)
  {
    double trace_angle;

    d_point pixel;

    tracestruct *trace;

    // set the beam ID
    actbeam->ID = conf->beam[beamID].ID;

    // set the model template ID's
    actbeam->modspec  = sobj->modspec;
    actbeam->modimage = sobj->modimage;

    // Adjust for posibly non (0,0) ref point of the 2D field dependence
    // get the geometrical description of the trace at position "pixel"
    pixel.x = sobj->xy_image.x - 1.0 - conf->refx;
    pixel.y = sobj->xy_image.y - 1.0 - conf->refy;
    trace = get_tracestruct_at_pos (conffile, conf->beam[beamID].ID, pixel);

    // transfer trace information to the beam
    actbeam->spec_trace = vector_to_trace_polyN(trace->pol);

    // compute the local trace angle
    trace_angle = atan2(actbeam->spec_trace->deriv (0, actbeam->spec_trace->data),1.0);

    // set the reference point
    actbeam->refpoint.x = sobj->xy_image.x - 1.0 + trace->offset.x;
    actbeam->refpoint.y = sobj->xy_image.y - 1.0 + trace->offset.y;

    // fill the flux and shape information
    SexMags_to_beamFlux(sobj, actbeam);

    // fill in the slit geometry
    SexObject_to_slitgeom(conf, sobj, trace_angle, actbeam);

    // actually set the extraction parameters
    set_extraction_parameters(sobj,bck_mode,mfwhm, auto_reorient, trace_angle, actbeam);

    // set the beam corners and the ignore flag
    fill_corner_ignore(sobj, obs, conf, beamID, dmag, actbeam);
  }

/**
 * Function: SexObject_to_objectII
 * Fill an object structure with the information contained in a SexObject
 * structure This function uses a configuration file to compute the full
 * list of beams for each object.
 *
 * Parameters:
 * @param sobj     - pointer to a  SexObject
 * @param obs      - a pointer to the data array containing the image
 * @param conf     - pointer to the configuration structure
 * @param conffile - the name of the aperture configuration file
 * @param mfwhm    - the fwhm multiplicator constant to apply to
 *                   determine the width of the aperture box for the object.
 * @param dmag     - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set then this task tries to optimize
 *                        the orientation of the
 *                        extraction slit - Should in general be left at 1 so that strange
 *                        geomety is avoided. If set to 2 the extraction is
 *                        forced to be vertical (90 deg.)
 * @param bck_mode - pointer for background mode
 *
 * Returns:
 * @return a pointer to a newly allocated object structure
 */
object * SexObject_to_objectII(SexObject * sobj, observation * const obs,
                               aperture_conf *conf, char conffile[], float mfwhm, float dmag,
                               int auto_reorient, int bck_mode)
  {
    int i=0;

    object *ob;

    // allocate an object
    ob = (object *)malloc(sizeof(object));

    // store the object specific
    // information
    ob->ID = sobj->number;
    ob->nbeams = conf->nbeams;
    ob->grism_obs = obs;

    // go over all beams
    for (i = 0; i < conf->nbeams; i++)
        // fill the current beam
        SexObject_to_beam(sobj, obs, conf, conffile, mfwhm, dmag, auto_reorient,
                          bck_mode, i, &(ob->beams[i]));

    // return the object
    return ob;
  }

/**
 * Function: SexObject_to_object
 * Fill an object structure with the information contained in a SexObject
 * structure This function uses a configuration file to compute the full
 * list of beams for each object.
 *
 * Parameters:
 * @param sobj - pointer to a  SexObject
 * @param obs  - a pointer to the data array containing the image
 * @param conffile - the name of the aperture configuration file
 * @param maxmag  - upper magniture bound. If object has a magnitude
 *                  greater than this has its ignore flag set to 1,
 *                  zero otherwise.
 * @param mfwhm - the fwhm multiplicator constant to apply to
 *                determine the width of the aperture box for the object.
 * @param dmag - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set then this task tries to optimize
 *                        the orientation of the
 *  extraction slit - Should in general be left at 1 so that strange
 *                    geomety is avoided. If set to 2 the extraction is
 *                    forced to be vertical (90 deg.)
 *
 * Returns:
 * @return a pointer to a newly allocated object structure
 *
 */
// object *
// SexObject_to_object (SexObject * sobj, observation * const obs,
//                      aperture_conf *conf, char conffile[], float mfwhm,
//                      float dmag,
//                      //              char conffile[], float mfwhm, float dmag,
//                      int auto_reorient, int bck_mode)
// {
//   int i, dx0, dx1;
//   beam *b;
//   object *ob = malloc (sizeof (object));
//   d_point pixel;
//   tracestruct *trace;
//   //  aperture_conf *conf;
//   float mmag_extract, mmag_mark;
//   //  conf = get_aperture_descriptor (conffile);
//   int j = 0, jj=0, nvalid=0;
//   double fval=0.0;
//   gsl_vector *flux;
//   float aposang, paposang;
//   float dya, dyb;
//   int iturn;

//   ob->ID = sobj->number;

//   for (i = 0; i < conf->nbeams; i++)
//     {
//       dx0 = conf->beam[i].offset.dx0;
//       dx1 = conf->beam[i].offset.dx1;
//       mmag_extract = conf->beam[i].mmag_extract+dmag;
//       mmag_mark = conf->beam[i].mmag_mark+dmag;
//       b = &(ob->beams[i]);
//       b->ID = conf->beam[i].ID;

//       //---------------------------------------
//       // some code for FORS2 MXU
//       // b->backwindow.x = sobj->backwindow.x;
//       // b->backwindow.y = sobj->backwindow.y;

//       b->modspec  = sobj->modspec;
//       b->modimage = sobj->modimage;

//       /* Adjust for posibly non (0,0) ref point of the 2D field dependence */
//       pixel.x = sobj->xy_image.x-1 - conf->refx;
//       pixel.y = sobj->xy_image.y-1 - conf->refy;
//       /* get the geometrical description of the trace at position "pixel" */
//       trace =
//         get_tracestruct_at_pos (conffile, conf->beam[i].ID, pixel);

//       b->spec_trace = vector_to_trace_polyN ( trace->pol );

//       b->width   = sobj->el_image.a * mfwhm;

//       /* if flux/wavelength information exists */
//       if (sobj->magnitudes)
//         {

//           // get the number of valid entries in 'sobj->magnitudes'
//           nvalid = get_valid_entries(sobj->magnitudes);
//           if (!nvalid)
//             aXe_message (aXe_M_WARN3, __FILE__, __LINE__,
//                          "No valid magnitude for object %i !\n", sobj->number);

//           // allocate the vector for the flux
//           flux = gsl_vector_alloc (2*(nvalid));

//           // fill the flux vector with values
//           jj=0;
//           for (j=0; j < (int)sobj->magnitudes->size; j++)
//             {
//               if (is_valid_entry(gsl_vector_get(sobj->magnitudes, j)))
//                 {

//                   fval = get_flambda_from_magab(gsl_vector_get(sobj->magnitudes, j), gsl_vector_get(sobj->lambdas, j));

//                   gsl_vector_set(flux, 2*jj, gsl_vector_get(sobj->lambdas, j));
//                   //              gsl_vector_set(flux, 2*jj+1, gsl_vector_get(sobj->magnitudes, j));
//                   gsl_vector_set(flux, 2*jj+1, fval);
//                   jj++;
//                 }
//             }
//           b->flux = flux;

//           /* fill the structual parameters of the object */
//           b->awidth  = sobj->el_image.a;
//           b->bwidth  = sobj->el_image.b;
//           b->aorient = sobj->el_image.theta;
//         }
//       // if flux/wavelength information does not exists
//       else
//         {
//           // set everything to dummy values
//           b->flux = NULL;

//           b->awidth  = -1.0;
//           b->bwidth  = -1.0;
//           b->aorient = -1.0;
//         }

//       /* Convert from SeXtractor angle reference point to aXe's */
//       b->orient = (180 + sobj->el_image.theta) / 180. * M_PI;
//       while (b->orient > M_PI)
//         b->orient = b->orient - M_PI;

//       // calculate the angle between
//       // the extraction direction and the trace
//       aposang =
//         b->orient - atan2(b->spec_trace->deriv (0, b->spec_trace->data),1.0);

//       // transform the angle to degrees
//       paposang = aposang / M_PI * 180.;

//       // give a warning for small angles
//       // between the orientation direction and
//       // the trace angle.
//       // BUGFIX comparison should be to paposang not the logical comparison MLS
//       if ( ( (fabs(paposang) < MIN_DIFFANGLE) || (fabs(paposang-180.0) < MIN_DIFFANGLE) ) && (auto_reorient == 0) )
//         aXe_message (aXe_M_WARN4, __FILE__, __LINE__,
//                      "aXe_GOL2AF: Object ID: %i: The angle between the extraction orientation and "
//                      "the trace is less than %5.2f degrees. You may get severe problems"
//                      " down to core dumps in the 1D extraction later on!\n", ob->ID, MIN_DIFFANGLE);

//        Case when the extraction angle is modified to that the extraction
//       /* proceeds along the semi-axis which projects farther away from the
//        * trace i.e. we avoid trying to extract spectra in a direction nearly
//        * parallel the trace */
//       if (auto_reorient==1)
//         {

//           aposang =
//             b->orient - atan2(b->spec_trace->deriv (0,
//                                                     b->spec_trace->data),1.0);

//           /* February 2004 introduced to make a hardstop in the range for
//              allowed angles. */
//           if (aposang > 0.0){
//             paposang = aposang / M_PI * 180.;
//           }
//           else{
//             paposang = aposang / M_PI * 180. + 360.0;
//           }
//           if (paposang > 180.0)
//             paposang = paposang-180.0;
//           /* the hard stop is here */
//           if (paposang > 30.0 && paposang < 150.0){
//             iturn = 0;}
//           else{
//             iturn = 1;}
//           /*end introduction */

//           /* Compute how far each axes extends away from the trace */
//           dya = fabs (sobj->el_image.a * sin (aposang));
//           dyb = fabs (sobj->el_image.b * sin (M_PI / 2. - aposang));

//           /* Select the broader of the two axes */
//           if (dya > dyb && iturn == 0)
//             {
//               b->width = sobj->el_image.a * mfwhm;
//             }
//           else
//             {
//               b->width = sobj->el_image.b * mfwhm;
//               b->orient = b->orient + M_PI / 2.;
//             }
//         }

//         /* Case when we force the extraction to be vertical,
//            the extraction width is recomputed */
//       if (auto_reorient==2) {
//         // new system for fixed extraction:
//         //    - extraction direction perpendicular
//         //      to the trace
//         //    - the mfwhm is used as a fixed extraction widt in pixels
//         b->orient =
//           atan2(b->spec_trace->deriv (0,b->spec_trace->data),1.0)+ M_PI / 2.0;

//         if (mfwhm < 0.0)
//           {
//             b->orient =
//               atan2(b->spec_trace->deriv (0.0,b->spec_trace->data),1.0)+ M_PI / 2.0;
//             b->width  = -mfwhm;

//           }
//         else
//           {
//             aposang = b->orient
//               - atan2(b->spec_trace->deriv (0,b->spec_trace->data),1.0);

//             /* Compute how far each axes extends away from the trace */
//             dya = fabs (sobj->el_image.a * sin (aposang));
//             dyb = fabs (sobj->el_image.b * sin (M_PI / 2. - aposang));
//             b->width = mfwhm*MAX(dya,dyb);
//             b->orient =
//               atan2(b->spec_trace->deriv (0,b->spec_trace->data),1.0)+ M_PI / 2.0;
//           }

//       }

//       if (sobj->backwindow.x != -1 && bck_mode)
//         b->width = MAX(b->width, sobj->backwindow.x);

//       /* On coordinate systems:
//          the sexrtractor image positions are given in the iraf system,
//          which means the value of the lower left pixel is associated
//          with the coordinate (1.0,1.0).
//          aXe works in a kind of 'matrix system', where the value of
//          the lower left pixel is stored in the matrix indices (0,0)
//          or (0.0,0.0) seen as a coordinate system.
//          To transform into this system 1.0 is subracted from
//          both sextractor coordinates.
//        */
//       b->refpoint.x = sobj->xy_image.x-1.0 + trace->offset.x;
//       b->refpoint.y = sobj->xy_image.y-1.0 + trace->offset.y;

//       /* magnitude cut */
//       // PROBLEM: there is a logical flaw inside
//       //          whhat happend when (mag > mag_mark) && (mag <  mmag_extract) ????
//       //          of course this has only relevance when mag_mark < mmag_extract
//       //      if ((sobj->mag_auto <= mmag_mark)&&(sobj->mag_auto <= mmag_extract))
//       // That's now an easy fix, but makes sense..
//       if (sobj->mag_auto <= mmag_extract)
//         {
//           b->ignore = 0; /* This object will be extracted */
//         }
//       if ((sobj->mag_auto <= mmag_mark)&&(sobj->mag_auto > mmag_extract))
//         {
//           b->ignore = 2; /* This object will be not be extracted */
//         }
//       if ((sobj->mag_auto > mmag_mark)&&(sobj->mag_auto > mmag_extract))
//         {
//           b->ignore = 1; /* This object will be ignored */
//         }

//       if (!fill_object_bbox (obs, b, 2, dx0, dx1))
//         b->ignore = 1;

//     b->slitgeom[0] = -1.0;
//     b->slitgeom[1] = -1.0;
//     b->slitgeom[2] = -1.0;
//     b->slitgeom[3] = -1.0;
//     }

//   ob->nbeams = conf->nbeams;
//   ob->grism_obs = obs;

//   return ob;
// }

/**
 * Function: SexObjects_to_oblist
 * Produces an object list from the data contained in an array of SexObjects
 *
 * Parameters:
 * @param sobjs - an NULL terminated array containing pointers to SexObjects
 * @param obs - a pointer to the data array containing the image
 * @param conffile - the name of the aperture configuration file
 * @param mmag_extract - upper magniture bound. Any object with a magnitude greater than
 *  this has its ignore flag set to 1.
 * @param mmag_mark - upper magniture bound. Any object with a magnitude greater than
 *  this has its ignore flag set to 2.
 * @param mfwhm - the fwhm multiplicator constant to apply to determine the width of the
 *  aperture box for the object.
 * @param dmag - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set to 1 then this task tries to optimize the orientation of the
 *  extraction slit. Should in general be left at 1 so that strange geomety is avoided. If set to 2
 *  the extraction is forced to be vertical (90 deg.)
 *
 *  Return:
 *  @return a pointer to a NULL terminated object array.
 */
object **
SexObjects_to_oblist (SexObject ** sobjs, observation * const obs,
                      aperture_conf *conf, char conffile[], float mfwhm,
                      float dmag,
                      //                      char conffile[], float mfwhm, float dmag,
                      int auto_reorient, int bck_mode)
{
     int i, nobjs = 0;
     object **oblist;

     /* Find the number of SexObjects in sobjs */
     while (sobjs[nobjs])
          nobjs++;
     /* Allocate enough room for a new object list */
     oblist = (object **) malloc ((nobjs + 1) * sizeof (object *));

     for (i = 0; i < nobjs; i++)
       {
         //fprintf(stdout, "Using the old routine...\n");
         oblist[i] =
         SexObject_to_objectII(sobjs[i], obs, conf, conffile, mfwhm, dmag, auto_reorient, bck_mode);
        }
     oblist[nobjs] = NULL;

     return oblist;
}

/**
 * Function: SexObjects_to_oblistII
 * Produces an object list from the data contained in an array of SexObjects
 *
 * Parameters:
 * @param sobjs - an NULL terminated array containing pointers to SexObjects
 * @param obs - a pointer to the data array containing the image
 * @param conffile - the name of the aperture configuration file
 * @param mmag_extract - upper magniture bound. Any object with a magnitude greater than
 *  this has its ignore flag set to 1.
 * @param mmag_mark - upper magniture bound. Any object with a magnitude greater than
 *  this has its ignore flag set to 2.
 * @param mfwhm - the fwhm multiplicator constant to apply to determine the width of the
 *  aperture box for the object.
 * @param dmag - number of magnitudes to add to the magnitudes cutoffs
 * @param auto_reorient - if set to 1 then this task tries to optimize the orientation of the
 *  extraction slit. Should in general be left at 1 so that strange geomety is avoided. If set to 2
 *  the extraction is forced to be vertical (90 deg.)
 *
 *  Return:
 *  @return a pointer to a NULL terminated object array.
 */
object **
SexObjects_to_oblistII (SexObject ** sobjs, observation * const obs,
                      aperture_conf *conf, char conffile[], float mfwhm,
                      float dmag,
                      int auto_reorient, int bck_mode)
{
     int i, nobjs = 0;
     object **oblist;
     fflush(stdout);
     /* Find the number of SexObjects in sobjs */
     while (sobjs[nobjs])
          nobjs++;
     /* Allocate enough room for a new object list */
     oblist = (object **) malloc ((nobjs + 1) * sizeof (object *));

     for (i = 0; i < nobjs; i++)
       {
         //fprintf(stdout, "Using the new routine...\n");
         oblist[i] =
         SexObject_to_objectII(sobjs[i], obs, conf, conffile, mfwhm, dmag, auto_reorient, bck_mode);
        }
     oblist[nobjs] = NULL;
     return oblist;
}

/**
 * Function: check_conf_for_slitlessgeom
 * The functions checks whether a smoothed flux conversion
 * is possible or not. In case that keywords in the configuration
 * files are missing, it is NOT possible, and 0 is returned.
 *
 * Parameters:
 * @param conf          - the configuarion file structure
 * @param auto_reorient - integer indicating the extraction method
 *
 * Returns
 * @return is_possible - the paramters used in the smoothing
 */
int
check_conf_for_slitlessgeom(const aperture_conf *conf, const int auto_reorient)
//check_conf_for_slitlessgeom(const aperture_conf *conf, const int slitless_geom)
  {
    int is_possible=1;

    if (auto_reorient == 1 && conf->pobjsize < 0.0)
      // change the switch
      is_possible = 0;

    // return the pointer
    return is_possible;
  }

/**
 * Function: fill_object_bbox
 * Fill up the aperture part of the beam of an object structure by looking at the trace polynomial
 * description associated with this object.
 *
 * Parameters:
 * @param obs - a pointer to the data array containing the image (used for bound checking)
 * @param b - A pointer to a beam whose aperture must be filled.
 * @param m_width - The width in pixel of the extraction box
 * @param dxmin - How far (in pixel) to follow the trace on the left side of the spectrum
 * @param dxmax - How far (in pixel) to follow the trace on the right hand side of the spectrum
 *
 * Returns:
 * @return 1 if bounding box appears to be valied (i.e. non-empty)
 */
int
fill_object_bbox (observation * const obs, beam * b, const float m_width,
                  const int dxmin, const int dxmax)
{
     d_point pmin, pmax;
     d_point pminl, pminh, pmaxl, pmaxh;
     float w = b->width/2. * m_width + 2.0;
     float wcos, wsin;
     float area;

     int xmin, xmax, xact;
     double yact;

     pmin.x = b->refpoint.x + dxmin;
     pmin.y =
          b->refpoint.y + b->spec_trace->func (dxmin, b->spec_trace->data);

     pmax.x = b->refpoint.x + dxmax;
     pmax.y =
          b->refpoint.y + b->spec_trace->func (dxmax, b->spec_trace->data);

     if (b->spec_trace->type > 1)
       {
         if (dxmin < dxmax)
           {
             xmin = dxmin;
             xmax = dxmax;
           }
         else
           {
             xmin = dxmax;
             xmax = dxmin;
           }

         for (xact = xmin; xact < xmax; xact++)
           {
             yact = b->refpoint.y + b->spec_trace->func (xact, b->spec_trace->data);

             if (yact < pmin.y)
               pmin.y = yact;
             if (yact > pmax.y)
               pmax.y = yact;
           }
         wcos = w * cos (b->orient);
         wsin = w * sin (b->orient);

         if (wcos > 0)
           {
             pmin.x = pmin.x - wcos;
             pmax.x = pmax.x + wcos;
           }
         else
           {
             pmin.x = pmin.x + wcos;
             pmax.x = pmax.x - wcos;
           }
         if (wsin > 0)
           {
             pmin.y = pmin.y - wsin;
             pmax.y = pmax.y + wsin;
           }
         else
           {
             pmin.y = pmin.y + wsin;
             pmax.y = pmax.y - wsin;
           }
         b->corners[0].x = pmin.x;
         b->corners[0].y = pmin.y;

         b->corners[1].x = pmax.x;
         b->corners[1].y = pmin.y;

         b->corners[2].x = pmax.x;
         b->corners[2].y = pmax.y;

         b->corners[3].x = pmin.x;
         b->corners[3].y = pmax.y;
       }
     else
       {

         wcos = w * cos (b->orient);
         wsin = w * sin (b->orient);

         pminl.x = pmin.x - wcos;
         pminl.y = pmin.y - wsin;

         pminh.x = pmin.x + wcos;
         pminh.y = pmin.y + wsin;

         pmaxl.x = pmax.x - wcos;
         pmaxl.y = pmax.y - wsin;

         pmaxh.x = pmax.x + wcos;
         pmaxh.y = pmax.y + wsin;

         b->corners[0].x = pminl.x;
         b->corners[0].y = pminl.y;

         b->corners[1].x = pmaxl.x;
         b->corners[1].y = pmaxl.y;

         b->corners[2].x = pmaxh.x;
         b->corners[2].y = pmaxh.y;

         b->corners[3].x = pminh.x;
         b->corners[3].y = pminh.y;
       }

     /* Compute the area of this aperture */
     area =
          0.5 * (abs (b->corners[0].y - b->corners[4].y) *
                 abs (b->corners[0].x - b->corners[1].x) +
                 abs (b->corners[2].y -
                       b->corners[1].y) * abs (b->corners[3].x -
                                               b->corners[2].x));
     if (area == 0)
       {
            fprintf (stderr, "aper debug: area is zero!\n");
            return 0;
       }
     return 1;
}

/**
 * Function: size_of_sextractor_catalog
 * A utility function which parses a Sextractor catalog file,
 * and returns the number of valid catalog entries found in the file
 *
 * Parameters:
 * @param filename - a pointer pointing to a char array containing the
 *                   list of a sextractor object output catalog.
 *                   Ignores rows starting with a ";"
 */
int
size_of_sextractor_catalog (char filename[])
{
  FILE *input;
  char Buffer[CATBUFFERSIZE];
  gsl_vector *v;
  int catsize;
  int num = 0;
  colinfo * actcatinfo;
  actcatinfo = get_sex_col_descr (filename);
  //  catalog_header = get_sex_col_descr (filename);
  catsize = actcatinfo->numcols;
  if (!(input = fopen (filename, "r")))
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Could not open Sextractor catalog" "file %s,\n",
                   filename);
    }

  while (fgets (Buffer, CATBUFFERSIZE, input))
    {
      if (Buffer[0] == ';')
        continue;
      lv1ws (Buffer);
      v = string_to_gsl_array (Buffer);
      if (v==NULL) continue;
      if ((int)v->size == catsize)
        num++;
    }
  return num;
}

/**
 * Function: get_SexObject_from_catalog
 * Parses a Sextractor 2.0 catalog file, and outputs a NULL terminated
 * array of SexObjects pointers. Ignores rows starting with a ;
 *
 * Parameters:
 * @param filename a pointer pointing to a char array containing the
 *                 list of a sextractor object output catalog
 *
 * Returns:
 * @return a pointer to an array of SeXObject pointer. NULL terminated
 */
SexObject **
get_SexObject_from_catalog (char filename[], const double lambda_mark)
{
  FILE *input;
  char Buffer[CATBUFFERSIZE];
  gsl_vector *v;
  gsl_vector *waves;
  gsl_vector *cnums;
  size_t hasmags=0;
  size_t nobjs;
  size_t catsize;
  int i;
  size_t magcencol=0;
  SexObject **sobjs, *sobj;
  colinfo * actcatinfo;
  px_point  backwin_cols;
  px_point  modinfo_cols;

  actcatinfo = get_sex_col_descr (filename);
  hasmags = has_magnitudes(actcatinfo);
  waves = gsl_vector_alloc (hasmags);
  cnums = gsl_vector_alloc (hasmags);
  hasmags = get_magcols(actcatinfo, waves, cnums);
  magcencol = get_magauto_col(waves, cnums, lambda_mark);
  backwin_cols = has_backwindow(actcatinfo);
  modinfo_cols = has_modelinfo(actcatinfo);

  //  catsize = count_keys (catalog_header);
  catsize = actcatinfo->numcols;
  nobjs = size_of_sextractor_catalog (filename);
  if (!(input = fopen (filename, "r")))
       {
         aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                      "Could not open Sextractor catalog" "file %s,\n",
                      filename);
       }

  /* Allocate enough room for nobjs+1 SexObject  pointers */
  sobjs = (SexObject **) malloc ((nobjs + 1) * sizeof (SexObject *));
  if (!sobjs)
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__, "Out of memory. Couldn't allocate Sextractor Object");
    }

  i = 0;
  while (fgets (Buffer, CATBUFFERSIZE, input))
    {
      if (Buffer[0] == ';')
        continue;
      v = string_to_gsl_array (Buffer);
      if (v==NULL) continue;
      if (v->size == catsize)
        {
          // BIG BUG!!!
          //               sobj = create_SexObject (catalog_header, Buffer);
          sobj = create_SexObject (actcatinfo, Buffer, waves, cnums, backwin_cols, modinfo_cols, magcencol, 0);
          sobjs[i++] = sobj;
        }
    }
  sobjs[i] = NULL;
  return sobjs;
}

/**
 * Function: el_to_ABC_world
 * Convert a wold coordinate ellipse into a set of 3 points on the
 * tangent plane point a is at pos of obj, semi major-axis end is point b,
 * semi minor axis is point c theta is measured from x-axis to semi-major
 * axis, counter clock wise.
 */
void
el_to_ABC_world (ellipse *el, sky_coord *a, sky_coord *b, sky_coord *c)
{

     a->ra = 0.0;
     a->dec = 0.0;

     b->ra = el->a * cos (el->theta / 180. * M_PI);
     b->dec = el->a * sin (el->theta / 180. * M_PI);

     c->ra = -1.0 * el->b * sin (el->theta / 180. * M_PI);
     c->dec = el->b * cos (el->theta / 180. * M_PI);
}

/*
 * Function: el_to_ABC_world2
 *
 */
void
el_to_ABC_world2 (ellipse *el, sky_coord *a, sky_coord *b, sky_coord *c)
{
  b->ra  = a->ra  - (el->a * cos (el->theta / 180. * M_PI))/cos(a->dec / 180. * M_PI);
  b->dec = a->dec + (el->a * sin (el->theta / 180. * M_PI));

  c->ra  = a->ra  - (el->b * cos ((el->theta-90.0)/180.*M_PI))/cos(a->dec / 180. * M_PI);
  c->dec = a->dec + (el->b * sin ((el->theta-90.0)/180.*M_PI));
}

/**
 * Function: ABC_image_to_el
 * Convert a set of three image coordinates into an elipse structure
 *
 */
void
ABC_image_to_el (d_point *a, d_point *b, d_point *c, ellipse *el)
{
  if (b->x != 0.0)
    {
      el->theta = atan (b->y / b->x) / M_PI * 180.0;
    }
  else
    {
      el->theta = 90.0;
    }

  el->a = sqrt ((b->x - a->x) * (b->x - a->x) + (b->y - a->y) * (b->y - a->y));
  el->b = sqrt ((c->x - a->x) * (c->x - a->x) + (c->y - a->y) * (c->y - a->y));
}

/**
 * Function: ABC_image_to_el2
 * Convert a set of three image coordinates into an elipse structure
 *
 */
void
ABC_image_to_el2 (d_point *a, d_point *b, d_point *c, ellipse *el)
{
  el->theta = atan ((b->y - a->y )/ (b->x - a->x)) / M_PI * 180.0;

  el->a = sqrt ((b->x - a->x) * (b->x - a->x) + (b->y - a->y) * (b->y - a->y));
  el->b = sqrt ((c->x - a->x) * (c->x - a->x) + (c->y - a->y) * (c->y - a->y));
}

/**
 *  Function: fill_missing_WCS_coordinates
 *  Update a SexObject structure and replace all missing WCS coordinates
 *  by re-computing them using the world one and the from_wcs wcs.
 *  Re-computes:
 *      peak_world_x
 *     peak_world.y
 *       xy_world.x
 *      xy_world.y
 *      el_world.a
 *      el_world.b
 *      el_world.theta
 *
 *  @param o - a pointer to an exsting SexObject. Should have been checked for validity already
 *  @param from_wcs - a pointer to an existing  WoldCoord wcs structure
 */
void
fill_missing_WCS_coordinates (SexObject * o, struct WorldCoor *from_wcs, int overwrite)
{
  sky_coord as, bs, cs;
  d_point a, b, c;
  //int offscl;

  /***************************************************************/
  /* If the WCS coordinates are missing, generate them using     */
  /* from_wcs and sextractor image coordinates                   */
  /***************************************************************/

  if ((overwrite) || (isnan (o->xy_world.ra)) || (isnan (o->xy_world.dec)))
    {
      /* recomputes xy_image  from the world values */
      /*pix2wcs (from_wcs, o->xy_image.x, o->xy_image.y,
               &(o->xy_world.ra),&(o->xy_world.dec), &offscl);
      */
      /* recomputes xy_image  from the world values */
      // new version requested since wcstools-3.6
      pix2wcs (from_wcs, o->xy_image.x, o->xy_image.y,
               &(o->xy_world.ra),&(o->xy_world.dec));
    }

  if ((isnan (o->el_world.a)) || (isnan (o->el_world.b))
      || (isnan (o->el_world.theta)) || (overwrite))
    {

      /* recomputes el_image them from the world values */

      /* Note the following is an unconventional use of the et_to*()
         routines since we store image coords in as, bs, and cs */
      el_to_ABC_world (&(o->el_image), &as, &bs, &cs);  /* Convert ellipse into a set of 3 coordinates */

      /* Compute the end point of the axes in the image coord system */
      /*      pix2wcs (from_wcs, as.ra, as.dec, &(a.x), &(a.y), &offscl);
      pix2wcs (from_wcs, bs.ra, bs.dec, &(b.x), &(b.y), &offscl);
      pix2wcs (from_wcs, cs.ra, cs.dec, &(c.x), &(c.y), &offscl);
      */
      /* Compute the end point of the axes in the image coord system */
      // new version requested since wcstools-3.6
      pix2wcs (from_wcs, as.ra, as.dec, &(a.x), &(a.y));
      pix2wcs (from_wcs, bs.ra, bs.dec, &(b.x), &(b.y));
      pix2wcs (from_wcs, cs.ra, cs.dec, &(c.x), &(c.y));

      ABC_image_to_el (&a, &b, &c, &(o->el_world));     /* convert 3 coordinates into ellipse */
    }
}

/**
 * Function: fill_missing_image_coordinates
 *  Update a SexObject structure and replace all missing image coordinates
 *  by re-computing them using the world one and the from_wcs wcs.
 *  Re-computes:
 *      peak_image_x
 *      peak_image.y
 *      xy_image.x
 *      xy_image.y
 *      el_image.a
 *      el_image.b
 *      el_image.theta
 *
 * Parameters:
 * @param o - a pointer to an exsting SexObject. Should have been
 *            checked for validity already
 * @param from_wcs - a pointer to an existing  WoldCoord wcs structure
 */
void
fill_missing_image_coordinates (SexObject *o, struct WorldCoor *from_wcs, int overwrite)
{
  sky_coord as, bs, cs;
  d_point a, b, c;
  int offscl;

  /************************/
  /* If the image coordinates are missing, generate them using from_wcs and sextractor world
     coordinates */
  /************************/
  if ((overwrite) || (isnan (o->xy_image.x)) || (isnan (o->xy_image.y)))
    {
      /* recomputes xy_image  from the world values */
      wcs2pix (from_wcs, o->xy_world.ra, o->xy_world.dec,
               &(o->xy_image.x), &(o->xy_image.y), &offscl);

    }

  if ((isnan (o->el_image.a)) || (isnan (o->el_image.b))
      || (isnan (o->el_image.theta)) || (overwrite))
    {

      /* recomputes el_image them from the world values */

      el_to_ABC_world (&(o->el_world), &as, &bs, &cs);  /* Convert ellipse into a set of 3 coordinates */

      /* Compute the end point of the axes in the image coord system */
      wcs2pix (from_wcs, as.ra, as.dec, &(a.x), &(a.y), &offscl);
      wcs2pix (from_wcs, bs.ra, bs.dec, &(b.x), &(b.y), &offscl);
      wcs2pix (from_wcs, cs.ra, cs.dec, &(c.x), &(c.y), &offscl);

      /* DISABLED !!*/
      //            ABC_image_to_el (&a, &b, &c, &(o->el_image));       /* convert 3 coordinates into ellipse */
    }
}

/**
 * Function: fill_all_missing_image_coordinates
 * Replaces all NaN values in a set of SexObjects with ones computed
 * from the available information and the associated WCS.
 *
 * Parameters:
 * @param sobjs    - an NULL terminated array containing pointers to SexObjects
 * @param from_wcs - a pointer to an existing  WoldCoord wcs structure
 * @param overwrite - forces the re-computation of all image coordinates
 *
 */
void
fill_all_missing_image_coordinates (SexObject ** sobjs,
                                    struct WorldCoor *from_wcs, int overwrite)
{
  int i, nobjs = 0;

  /* Find the number of SexObjects in sobjs */
  while (sobjs[nobjs])
    nobjs++;

  for (i = 0; i < nobjs; i++)
    {
      fill_missing_image_coordinates (sobjs[i], from_wcs, overwrite);
    }

}

/**
 * Function: fill_all_missing_WCS_coordinates
 *  Replaces all NaN values in a set of SexObjects with ones computed from the
 *  available information and the associated WCS.
 *
 * Parameters:
 *  @param sobjs - an NULL terminated array containing pointers to SexObjects
 *  @param from_wcs - a pointer to an existing  WoldCoord wcs structure
 *  @param overwrite - forces the re-computation of all image coordinates
 *
 */
void
fill_all_missing_WCS_coordinates (SexObject ** sobjs,
                                    struct WorldCoor *from_wcs, int overwrite)
{
  int i, nobjs = 0;

  /* Find the number of SexObjects in sobjs */
  while (sobjs[nobjs])
    nobjs++;

  for (i = 0; i < nobjs; i++)
    {
      fill_missing_WCS_coordinates (sobjs[i], from_wcs, overwrite);
    }

}

/**
 * Function: compute_new_image_coordinates
 *  Function that uses the exisiting world coordinates of a SexObject to
 *  compute new pixel coordinates using a (new) WCS.
 *
 * Parameters:
 *  @param o - a pointer to an exsting SexObject. Should have been checked
 *   for validity already
 *  @param to_wcs - a pointer to an existing  WoldCoord wcs structure
 *
 */
void
compute_new_image_coordinates (SexObject * o, struct WorldCoor *to_wcs)
{
  sky_coord as, bs, cs;
  d_point a, b, c;
  int offscl;

  /******************************************************************/
  /* uses the existing world coordinates of a SexObject to compute  */
  /* new pixel coordinates using a (new) WCS.                       */
  /*******************************************************************/

  /* recomputes xy_image  from the world values */
  wcs2pix (to_wcs, o->xy_world.ra, o->xy_world.dec, &(o->xy_image.x),
           &(o->xy_image.y), &offscl);

  /* recomputes el_image them from the world values */
  el_to_ABC_world (&(o->el_world), &as, &bs, &cs);      /* Convert ellipse into a set of 3 coordinates */

  /* Compute the end point of the axes in the image coord system */
  wcs2pix (to_wcs, as.ra, as.dec, &(a.x), &(a.y), &offscl);
  wcs2pix (to_wcs, bs.ra, bs.dec, &(b.x), &(b.y), &offscl);
  wcs2pix (to_wcs, cs.ra, cs.dec, &(c.x), &(c.y), &offscl);

  /* DISABLED */
  //     ABC_image_to_el (&a, &b, &c, &(o->el_image));  /* convert 3 coordinates into ellipse */
}

/**
 * Function: compute_new_image_sexobject
 * Function that uses the exisiting world coordinates of a SexObject to
 * compute new pixel coordinates using a (new) WCS.
 *
 * Parameters:
 * @param o - a pointer to an exsting SexObject. Should have been checked
 *  for validity already
 *  @param to_wcs - a pointer to an existing  WoldCoord wcs structure
 */
void
compute_new_image_sexobject (SexObject * o, struct WorldCoor *to_wcs, int th_sky)
{
  sky_coord  bs, cs;
  d_point a, b, c;
  int offscl;

  /******************************************************************/
  /* uses the existing world coordinates of a SexObject to compute  */
  /* new pixel coordinates using a (new) WCS.                       */
  /******************************************************************/

  /* recomputes xy_image  from the world values */
  wcs2pix (to_wcs, o->xy_world.ra, o->xy_world.dec, &(o->xy_image.x),
           &(o->xy_image.y), &offscl);

  /* Convert ellipse into a set of 3 coordinates */
  el_to_ABC_world2 (&(o->el_world), &(o->xy_world), &bs, &cs);

  /* Compute the end point of the axes in the image coord system */

  wcs2pix (to_wcs, o->xy_world.ra, o->xy_world.dec, &(a.x), &(a.y), &offscl);
  wcs2pix (to_wcs, bs.ra, bs.dec, &(b.x), &(b.y), &offscl);
  wcs2pix (to_wcs, cs.ra, cs.dec, &(c.x), &(c.y), &offscl);

  /* convert 3 coordinates into ellipse */
  ABC_image_to_el2 (&a, &b, &c, &(o->el_image));
  if (th_sky)
    o->el_image.theta = o->el_image.theta - 90.0;
}

/**
 * Function: compute_all_new_image_coordinates
 * Replaces all SexObjects image coordinates with new ones computed using  the
 * the passed WCS.
 *
 * Parameters:
 * @param sobjs - a NULL terminated array containing pointers to SexObjects
 * @param to_wcs - a pointer to an existing  WoldCoord wcs structure
 */
void
compute_all_new_image_coordinates (SexObject ** sobjs,
                                   struct WorldCoor *to_wcs)
{
     int i, nobjs = 0;

     /* Find the number of SexObjects in sobjs */
     while (sobjs[nobjs])
          nobjs++;

     for (i = 0; i < nobjs; i++)
       {
            compute_new_image_coordinates (sobjs[i], to_wcs);
       }

}

/**
 * Function: free_SexObjects
 *    Free a NULL terminated array of SexObject
 *
 * Parameters:
 *  @param sobjs - a NULL terminated array containing pointers to SexObjects
 *
 */
void
free_SexObjects (SexObject ** sobjs)
{
  int i, nobjs = 0;

  /* Find the number of SexObjects in sobjs */
  while (sobjs[nobjs])
    nobjs++;

  for (i = 0; i < nobjs; i++)
    {
      if (sobjs[i]->magnitudes){
        gsl_vector_free (sobjs[i]->lambdas);
        gsl_vector_free (sobjs[i]->magnitudes);
      }
      free (sobjs[i]);
      sobjs[i] = NULL;
    }
  free (sobjs);
  sobjs = NULL;
}


/* PETSC_DEPTRECATED causes parse erros in bindgen */
#define PETSC_DEPRECATED(arg)
/* Not sure how this is generated... */
#define PETSC_BITS_PER_BYTE 8
#include <petsc.h>


#pragma once
#include <gsl/gsl>
#include <memory>
class BassEnhance;
class Eq_5band;
class Reverb;

struct Fx_Parameter {
    const char *name;
    int default_value;
    enum Type { Integer, Boolean };
    Type type;
    enum Flag { HasSeparator = 1 };
    unsigned flags;
};

class Synth_Fx {
public:
    Synth_Fx();
    ~Synth_Fx();

    void init(float sample_rate);
    void clear();
    void compute(float data[], unsigned nframes);

    int get_parameter(size_t index) const;
    void set_parameter(size_t index, int value);

    static gsl::span<const Fx_Parameter> parameters();

    enum {
        P_Bass_Enhance,
        P_Bass_Amount,
        P_Bass_Tone,
        P_Eq_Enable,
        P_Eq_Low,
        P_Eq_Mid_Low,
        P_Eq_Mid,
        P_Eq_Mid_High,
        P_Eq_High,
        P_Reverb_Enable,
        P_Reverb_Amount,
        P_Reverb_Size,
        Parameter_Count,
    };

private:
    std::unique_ptr<BassEnhance> be_;
    std::unique_ptr<Eq_5band> eq_;
    std::unique_ptr<Reverb> rev_;
    bool be_enable_ = false;
    bool eq_enable_ = false;
    bool rev_enable_ = false;
};


/* rng/default.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
 *
 * 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 3 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_errno.h>

/* The initial defaults are defined in the file mt.c, so we can get
   access to the static parts of the default generator. */

const gsl_rng_type *
gsl_rng_env_setup (void)
{
  unsigned long int seed = 0;
  const char *p = getenv ("GSL_RNG_TYPE");

  if (p)
    {
      const gsl_rng_type **t, **t0 = gsl_rng_types_setup ();

      gsl_rng_default = 0;

      /* check GSL_RNG_TYPE against the names of all the generators */

      for (t = t0; *t != 0; t++)
        {
          if (strcmp (p, (*t)->name) == 0)
            {
              gsl_rng_default = *t;
              break;
            }
        }

      if (gsl_rng_default == 0)
        {
          int i = 0;

          fprintf (stderr, "GSL_RNG_TYPE=%s not recognized\n", p);
          fprintf (stderr, "Valid generator types are:\n");

          for (t = t0; *t != 0; t++)
            {
              fprintf (stderr, " %18s", (*t)->name);

              if ((++i) % 4 == 0)
                {
                  fputc ('\n', stderr);
                }
            }

          fputc ('\n', stderr);

          GSL_ERROR_VAL ("unknown generator", GSL_EINVAL, 0);
        }

      fprintf (stderr, "GSL_RNG_TYPE=%s\n", gsl_rng_default->name);
    }
  else
    {
      gsl_rng_default = gsl_rng_mt19937;
    }

  p = getenv ("GSL_RNG_SEED");

  if (p)
    {
      seed = strtoul (p, 0, 0);
      fprintf (stderr, "GSL_RNG_SEED=%lu\n", seed);
    };

  gsl_rng_default_seed = seed;

  return gsl_rng_default;
}


// Based on code found in https://github.com/deeplearning4j/libnd4j/blob/master/blas/cpu/NativeBlas.cpp

#include <cblas.h>

#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif

static int maxThreads = -1;
static int vendor = 0;

static void blas_set_num_threads(int num) {
    typedef void* (*void_int)(int);
    typedef int* (*int_int)(int);
    typedef int* (*int_int_int)(int, int);

    maxThreads = num;
#ifdef __MKL
    // if we're linked against mkl - just go for it
    MKL_Set_Num_Threads(num);
    MKL_Domain_Set_Num_Threads(num, 0); // MKL_DOMAIN_ALL
    MKL_Domain_Set_Num_Threads(num, 1); // MKL_DOMAIN_BLAS
    MKL_Set_Num_Threads_Local(num);
#elif __OPENBLAS
#ifdef _WIN32
    // for win32 we just check for mkl_rt.dll
    HMODULE handle = LoadLibrary("mkl_rt.dll");
    if (handle != NULL) {
        void_int mkl_global = (void_int) GetProcAddress(handle, "MKL_Set_Num_Threads");
        if (mkl_global != NULL) {
            mkl_global(num);

            vendor = 3;

            int_int_int mkl_domain = (int_int_int) GetProcAddress(handle, "MKL_Domain_Set_Num_Threads");
            if (mkl_domain != NULL) {
                mkl_domain(num, 0); // DOMAIN_ALL
                mkl_domain(num, 1); // DOMAIN_BLAS
            }

            int_int mkl_local = (int_int) GetProcAddress(handle, "MKL_Set_Num_Threads_Local");
            if (mkl_local != NULL) {
                mkl_local(num);
            }
        } else {
            printf("Unable to tune runtime. Please set OMP_NUM_THREADS manually.\n");
        }
        //FreeLibrary(handle);
    } else {
      // OpenBLAS path
      handle = LoadLibrary("libopenblas.dll");
      if (handle != NULL) {
        void_int oblas = (void_int) GetProcAddress(handle, "openblas_set_num_threads");
        if (oblas != NULL) {
            vendor = 2;
            oblas(num);
        } else {
            printf("Unable to tune runtime. Please set OMP_NUM_THREADS manually.\n");
        }
        //FreeLibrary(handle);
      } else {
        printf("Unable to guess runtime. Please set OMP_NUM_THREADS manually.\n");
      }
    }
#else
    // it's possible to have MKL being loaded at runtime
    void *handle = dlopen("libmkl_rt.so", RTLD_NOW|RTLD_GLOBAL);
    if (handle == NULL) {
        handle = dlopen("libmkl_rt.dylib", RTLD_NOW|RTLD_GLOBAL);
    }
    if (handle != NULL) {

        // we call for openblas only if libmkl isn't loaded, and openblas_set_num_threads exists
        void_int mkl_global = (void_int) dlsym(handle, "MKL_Set_Num_Threads");
        if (mkl_global != NULL) {
            // we're running against mkl
            mkl_global((int) num);

            vendor = 3;

            int_int_int mkl_domain = (int_int_int) dlsym(handle, "MKL_Domain_Set_Num_Threads");
            if (mkl_domain != NULL) {
                mkl_domain(num, 0); // DOMAIN_ALL
                mkl_domain(num, 1); // DOMAIN_BLAS
            }

            int_int mkl_local = (int_int) dlsym(handle, "MKL_Set_Num_Threads_Local");
            if (mkl_local != NULL) {
                mkl_local(num);
            }
        } else {
            printf("Unable to tune runtime. Please set OMP_NUM_THREADS manually.\n");
        }
        dlclose(handle);
    } else {
        // we're falling back to bundled OpenBLAS opening libopenblas.so.0
        handle = dlopen("libopenblas.so.0", RTLD_NOW|RTLD_GLOBAL);
        if (handle == NULL) {
            handle = dlopen("libopenblas.so", RTLD_NOW|RTLD_GLOBAL);
        }
        if (handle == NULL) {
            handle = dlopen("libopenblas.dylib", RTLD_NOW|RTLD_GLOBAL);
        }

        if (handle != NULL) {
            void_int oblas = (void_int) dlsym(handle, "openblas_set_num_threads");
            if (oblas != NULL) {
                vendor = 2;
                // we're running against openblas
                oblas((int) num);
            } else {
                printf("Unable to tune runtime. Please set OMP_NUM_THREADS manually.\n");
            }

            dlclose(handle);
        } else printf("Unable to guess runtime. Please set OMP_NUM_THREADS manually.\n");
    }
#endif

#else
    printf("Unable to guess runtime. Please set OMP_NUM_THREADS or equivalent manually.\n");
#endif
    fflush(stdout);
}

static int blas_get_num_threads() {
    return maxThreads;
}

/**
 *  0 - Unknown
 *  1 - cuBLAS
 *  2 - OpenBLAS
 *  3 - MKL
 */
static int blas_get_vendor() {
    return vendor;
}


/* rng/rng.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
 *
 * 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 3 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>

gsl_rng *
gsl_rng_alloc (const gsl_rng_type * T)
{

  gsl_rng *r = (gsl_rng *) malloc (sizeof (gsl_rng));

  if (r == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for rng struct",
                        GSL_ENOMEM, 0);
    };

  r->state = malloc (T->size);

  if (r->state == 0)
    {
      free (r);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for rng state",
                        GSL_ENOMEM, 0);
    };

  r->type = T;

  gsl_rng_set (r, gsl_rng_default_seed);        /* seed the generator */

  return r;
}

int
gsl_rng_memcpy (gsl_rng * dest, const gsl_rng * src)
{
  if (dest->type != src->type)
    {
      GSL_ERROR ("generators must be of the same type", GSL_EINVAL);
    }

  memcpy (dest->state, src->state, src->type->size);

  return GSL_SUCCESS;
}

gsl_rng *
gsl_rng_clone (const gsl_rng * q)
{
  gsl_rng *r = (gsl_rng *) malloc (sizeof (gsl_rng));

  if (r == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for rng struct",
                        GSL_ENOMEM, 0);
    };

  r->state = malloc (q->type->size);

  if (r->state == 0)
    {
      free (r);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for rng state",
                        GSL_ENOMEM, 0);
    };

  r->type = q->type;

  memcpy (r->state, q->state, q->type->size);

  return r;
}

void
gsl_rng_set (const gsl_rng * r, unsigned long int seed)
{
  (r->type->set) (r->state, seed);
}

unsigned long int
gsl_rng_max (const gsl_rng * r)
{
  return r->type->max;
}

unsigned long int
gsl_rng_min (const gsl_rng * r)
{
  return r->type->min;
}

const char *
gsl_rng_name (const gsl_rng * r)
{
  return r->type->name;
}

size_t
gsl_rng_size (const gsl_rng * r)
{
  return r->type->size;
}

void *
gsl_rng_state (const gsl_rng * r)
{
  return r->state;
}

void
gsl_rng_print_state (const gsl_rng * r)
{
  size_t i;
  unsigned char *p = (unsigned char *) (r->state);
  const size_t n = r->type->size;

  for (i = 0; i < n; i++)
    {
      /* FIXME: we're assuming that a char is 8 bits */
      printf ("%.2x", *(p + i));
    }

}

void
gsl_rng_free (gsl_rng * r)
{
  RETURN_IF_NULL (r);
  free (r->state);
  free (r);
}


/* bst/gsl_bst_rb.h
 *
 * Copyright (C) 2018 Patrick Alken
 *
 * 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 3 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BST_RB_H__
#define __GSL_BST_RB_H__

#include <gsl/gsl_math.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

#ifndef GSL_BST_RB_MAX_HEIGHT
#define GSL_BST_RB_MAX_HEIGHT 48
#endif

/* red-black node */
struct gsl_bst_rb_node
{
  struct gsl_bst_rb_node *rb_link[2]; /* subtrees */
  void *rb_data;                      /* pointer to data */
  unsigned char rb_color;             /* color */
};

/* red-black tree data structure */
typedef struct
{
  struct gsl_bst_rb_node *rb_root;   /* tree's root */
  gsl_bst_cmp_function *rb_compare;  /* comparison function */
  void *rb_param;                    /* extra argument to |rb_compare| */
  const gsl_bst_allocator *rb_alloc; /* memory allocator */
  size_t rb_count;                   /* number of items in tree */
  unsigned long rb_generation;       /* generation number */
} gsl_bst_rb_table;

/* red-black traverser structure */
typedef struct
{
  const gsl_bst_rb_table *rb_table;  /* tree being traversed */
  struct gsl_bst_rb_node *rb_node;   /* current node in tree */
  struct gsl_bst_rb_node *rb_stack[GSL_BST_RB_MAX_HEIGHT];
                                     /* all the nodes above |rb_node| */
  size_t rb_height;                  /* number of nodes in |rb_parent| */
  unsigned long rb_generation;       /* generation number */
} gsl_bst_rb_traverser;

__END_DECLS

#endif /* __GSL_BST_RB_H__ */