#ifndef __INVERTMOTORCONTROLLER_H
#define __INVERTMOTORCONTROLLER_H

#include "abstractcontroller.h"
#include "controller_misc.h"
#include <stdlib.h>
#include <string.h>

/**
 * Abstract class (interface) for robot controller that use direct matrix inversion and
 * simple one layer networks.
 * 
 * Implements standart parameters: epsC, rho, mu, stepnumber4avg, stepnumber4delay
 */
class InvertMotorController : public AbstractController {
public:
  InvertMotorController( unsigned short buffersize ){
    this->buffersize = buffersize;
    epsC=0.1;    // base learning rate
    desens=0.0; // name form desensitization
    s4delay=1;
    s4avg=1;
    steps=1;  
    epsA=0.1; // learning rate factor for model learning
    dampA=0;  // no damping
    factorB=0.2; // additional learning rate factor for model bias 
    zetaupdate=0; // don't use the zeta update line
    squashSize=0.01; // maximum size of update components
    logaE=0; // don't use logarithmic error ( ln(v^T*v) )
    rootE=0; // don't use logarithmic error ( (v^T*v)^(1/2) )
    relativeE=0; // don't use relative modelling error 
    adaptRate=0.005; // factor used for adapting the lerning rates.
    nomUpdate=0.005; // size of nominal update in respect to matrix norm 
    noiseB = 0.001;   // noise that is added to bias update (before factorB is applied)
    teacher = 5;      // factor for teaching signal
    t=0;
    initialised = false;
  }

  virtual paramval getParam(const paramkey& key) const{
    if(key == "epsC") return epsC; 
    else if(key == "epsA") return epsA; 
    else if(key == "dampA") return dampA;
    else if(key == "adaptrate") return adaptRate;
    else if(key == "nomupdate") return nomUpdate;
    else if(key == "desens") return desens; 
    else if(key == "s4delay") return s4delay; 
    else if(key == "s4avg") return s4avg; 
    else if(key == "steps") return steps; 
    else if(key == "zetaupdate") return zetaupdate; 
    else if(key == "squashsize") return squashSize; 
    else if(key == "logaE") return logaE; 
    else if(key == "rootE") return rootE;
    else if(key == "relativeE") return relativeE;
    else if(key == "factorB") return factorB; 
    else if(key == "noiseB") return noiseB;
    else if(key == "teacher") return teacher;
    else  return AbstractController::getParam(key);
  }

  virtual bool setParam(const paramkey& key, paramval val){
    if(key == "epsC") epsC=val;
    else if(key == "epsA") epsA=val;
    else if(key == "dampA") dampA=val;
    else if(key == "adaptrate") adaptRate=val;
    else if(key == "nomupdate") nomUpdate=val;
    else if(key == "desens") desens=val;
    else if(key == "s4delay") s4delay=val; 
    else if(key == "s4avg") s4avg=val;
    else if(key == "steps") steps=val;
    else if(key == "zetaupdate") zetaupdate=val;
    else if(key == "squashsize") squashSize=val;
    else if(key == "logaE") logaE=(short)val;
    else if(key == "rootE") rootE=(short)val;
    else if(key == "relativeE") relativeE=(short)val;
    else if(key == "factorB") factorB=val;
    else if(key == "noiseB") noiseB=val;
    else if(key == "teacher") teacher=val;
    else return AbstractController::setParam(key, val);
    //    else fprintf(stderr, "parameter %s unknown\n", key);
    return true;
  }

  virtual paramlist getParamList() const{
    paramlist list;
    list.push_back(pair<paramkey, paramval> ("epsA", epsA));
    list.push_back(pair<paramkey, paramval> ("epsC", epsC));
    list.push_back(pair<paramkey, paramval> ("dampA", dampA));
    list.push_back(pair<paramkey, paramval> ("adaptrate", adaptRate));
    list.push_back(pair<paramkey, paramval> ("nomupdate", nomUpdate));
    list.push_back(pair<paramkey, paramval> ("desens", desens));
    list.push_back(pair<paramkey, paramval> ("s4delay", s4delay ));
    list.push_back(pair<paramkey, paramval> ("s4avg", s4avg));
    list.push_back(pair<paramkey, paramval> ("steps", steps));
    list.push_back(pair<paramkey, paramval> ("zetaupdate", zetaupdate));
    list.push_back(pair<paramkey, paramval> ("squashsize", squashSize));
    list.push_back(pair<paramkey, paramval> ("logaE", logaE));
    list.push_back(pair<paramkey, paramval> ("rootE", rootE));
    list.push_back(pair<paramkey, paramval> ("relativeE", relativeE));
    list.push_back(pair<paramkey, paramval> ("factorB", factorB));
    list.push_back(pair<paramkey, paramval> ("noiseB", noiseB));
    list.push_back(pair<paramkey, paramval> ("teacher", teacher));
    return list;
  }

protected:
  paramval epsC; //< learning rate factor for controller learning
  paramval desens;
  paramval s4delay; //< number of timesteps of delay in the SML 
  paramval s4avg; //< number of timesteps used for smoothing the controller output values 
  paramval steps; //< number of timesteps is used for controller learning
  paramval epsA; //< learning rate factor for model learning
  paramval factorB; //< additional learning rate factor for model bias 
  paramval zetaupdate;
  paramval dampA; //< damping term for model (0: no damping, 0.1 high damping)
  short logaE;  //< logarithmic error is used for learning 1: controller 2: model 3: both
  short rootE;  //< root error is used for learning 1: controller 2: model 3: both
  short relativeE; //< if not 0: a relative error signal is used (xsi is normalised in respect to |y|)

  paramval squashSize; //< size of the box, where the parameter updates are clipped to
  paramval adaptRate; //< adaptation rate for learning rate adaptation
  paramval nomUpdate; //< nominal update of controller in respect to matrix norm
  paramval noiseB;    //< size of the additional noise for model bias B
  paramval teacher; //< factor for teaching signal

  int t;
  unsigned short buffersize;
  string name;
  bool initialised;

protected:
  // put new value in ring buffer
  void putInBuffer(Matrix* buffer, const Matrix& vec, int delay = 0){
    buffer[(t-delay)%buffersize] = vec;
  }

  /// neuron transfer function
  static double g(double z)
  {
    return tanh(z);
  };

  /// first dervative
  static double g_s(double z)
  {
    double k=tanh(z); 
    return 1.025 - k*k; 
    //return 1.2 - k*k; 
    // return 1/(1+z*z);    // softer
    //return 1/(1+log(1+z*z)); // even softer
  };

  /// inverse of the first derivative
  static double g_s_inv(double z)
  {
    double k=tanh(z);
    return 1/(1.025 - k*k);
    //return 1/(1.2 - k*k);
    // return 1+z*z; // softer
    //return 1+log(1+z*z); // even softer
  };

  /// squashing function, to protect against to large weight updates
  static double squash(double z)
  {
    return z < -0.1 ? -0.1 : ( z > 0.1 ? 0.1 : z );
    //return 0.1 * tanh(10.0 * z);
  };

};

#endif
