#ifndef __UTIL__FUNCTION_HPP__
#define __UTIL__FUNCTION_HPP__
/**
 * \file function.h
 *
 * Defines the Function class
 */

#include <Config.hpp>

#include <QString>

#include <string>
#include <vector>
#include <Vector.hpp>

namespace mgx
{
  /**
   * \class Function Function.hpp <function.hpp>
   * \brief A utility class for functions.
   *
   * This class is a function object that encapsulates functions
   * in the VLAB function formats (original and fver 1 1).
  */
  class mgx_EXPORT Function
  {
  public:
    Function();
    Function(const QString &filename);
    Function(const Function& copy);
    Function& operator=(const Function&);
#ifdef USE_CXX0X
    Function(Function&& copy) = default;
    Function& operator=(Function&&) = default;
#endif
    double operator()(double x);
    const Vector<2,double>& getMax() const;
    const Vector<2,double>& getMin() const;
    void reRead();
    bool setFile(const QString &file);
    void setSamples (size_t n);
    bool error();
    QString fileName() { return filename; }
    /**
     * Scale the x axis by s
     * \param s Scaling factor to apply to the axis
     */
    void scaleX(double s) { scaling_x = 1/s; }
    /**
     * Scale the y axis by s
     * \param s Scaling factor to apply to the axis
     */
    void scaleY(double s) { scaling_y = s; }
    /// Get the current x scaling
    double scaleX() const { return 1/scaling_x; }
    /// Get the current y scaling
    double scaleY() const { return scaling_y; }
    /**
     * Shift the x axis by s
     * \param s Shift of the axis
     *
     * Note that the shift happens after the scaling, so it should be written 
     * in the scaled reference system.
     */
    void shiftX(double s) { shift_x = -s; }
    /**
     * Shift the y axis by s
     * \param s Shift of the axis
     *
     * Note that the shift happens after the scaling, so it should be written 
     * in the scaled reference system.
     */
    void shiftY(double s) { shift_y = s; }
    /// Get the current x axis shift
    double shiftX() const { return -shift_x; }
    /// Get the current y axis shift
    double shiftY() const { return shift_y; }
    void normalizeY(bool shift = true);
    void normalizeX(bool shift = true);

  private:
    QString filename;
    std::vector<Vector<2,double> > pts;
    Vector<2,double> max;
    Vector<2,double> min;
    unsigned int samples;
    double scaling_x, scaling_y;
    double shift_x, shift_y;

    Vector<2,double> P(double x) const;
    double N(int, int, double) const;
    double Nk1(int, double) const;
    double Nkt(int, int, double) const;
    int    Uk(int) const;
    double getVal(double x) const; // computes the actual value for a given x

    struct CacheVal {
      bool valid;
      double val;
    };
    bool cache_valid;              // whether the cache vector is resized properly or not
    std::vector <CacheVal> cache;  // stores the cached values
    void init();                   // should be executed by every constructor
    bool error_occured;            // when loading this indicates whether there was an error
  };
}

#endif
