#ifndef __MULTILAYERFFNN_H
#define __MULTILAYERFFNN_H

#include <vector>

#include "feedforwardnn.h"

class Layer {
public:
  Layer(int size, double factor_bias=0.1, 
	ActivationFunction actfun = FeedForwardNN::linear, 
	ActivationFunction dactfun = FeedForwardNN::dlinear)
    : size(size), factor_bias(factor_bias), actfun(actfun), dactfun(dactfun) {}

  int size;
  double factor_bias;
  ActivationFunction actfun;  //< callback activation function
  ActivationFunction dactfun; //< first derivative of the activation function  
};

/// multi layer neural network with configurable activation functions
class MultiLayerFFNN : public FeedForwardNN {
public: 
  /**
     @param eps learning rate
     @param layers Layer description (the input layer is not specified (always linear))
  */
  MultiLayerFFNN(double eps, 
		 const vector<Layer>& layers)
    : eps(eps), layers(layers) { 
    
    initialised = false;  
  }
  virtual ~MultiLayerFFNN(){ }

  /// initialisation of the network with the given number of input and output units
  virtual void init(unsigned int inputDim, unsigned  int outputDim); 

  /// passive processing of the input
  virtual const matrix::Matrix process (const matrix::Matrix& input) const; 

  /// performs learning and returns the network output before learning
  virtual const matrix::Matrix learn (const matrix::Matrix& input, 
				      const matrix::Matrix& nom_output, 
				      double learnRateFactor = 1);

  /// returns the number of input neurons
  virtual unsigned int getInputDim() const { 
    return weights[0].getN(); 
  }
  /// returns the number of output neurons
  virtual unsigned int getOutputDim() const { 
    return (weights.rbegin())->getM(); 
  }

  /// damps the weights and the biases by multiplying (1-damping)
  virtual void damp(double damping);

  /************** CONFIGURABLE INTERFACE ************************/
  virtual paramkey getName() const {
    return string("multilayerffnn");
  }

  virtual paramval getParam(const paramkey key) const {
    if(key == "eps") return eps;
    else return 0.0;
  }
  
  virtual bool setParam(const paramkey key, paramval val){
    if( key == "eps") { 
      eps = val;
      return true;
    } else return false;
  }

  virtual paramlist getParamList() const{
    paramlist l;
    l.push_back(pair<paramkey, paramval> ("eps", eps));
    return l;
  }


private:
  double eps;
  vector<Layer> layers;
  vector<matrix::Matrix> weights;
  vector<matrix::Matrix> bias;

  bool initialised;
};

#endif
