#ifndef LAYER_HH
#define LAYER_HH

#include <string>
#include <map>
#include <vector>

#include "Params.hh"
#include "loggers.hh"

class OCTETSTRING;
class BITSTRING;
class CHARSTRING;
class INTEGER;

class Layer {
  std::vector<Layer*> upperLayers;
  std::vector<Layer*> lowerLayers;
protected:
  std::string type;
public:
  Layer() : upperLayers(), lowerLayers(), type(std::string("")) { loggers::get_instance().log("Layer::Layer (D)"); };
  Layer(const std::string& p_type) : upperLayers(), lowerLayers(), type(std::string(p_type.begin(), p_type.end())) { loggers::get_instance().log("Layer::Layer"); };
  virtual ~Layer() { upperLayers.clear(); lowerLayers.clear(); };
  void deleteLayer() { };
public:
  inline void addUpperLayer(Layer* p_layer) { 
    //loggers::get_instance().log(">>> Layer::addUpperLayer");
    if (p_layer != NULL) {
      loggers::get_instance().log("Layer::addUpperLayer: %s is upper layer of %s", p_layer->to_string().c_str(), to_string().c_str());
      upperLayers.push_back(p_layer);
      loggers::get_instance().log(" Layer::addUpperLayer: %s is lower layer of %s", to_string().c_str(), p_layer->to_string().c_str());
      p_layer->lowerLayers.push_back(this);
    };
  };
  void removeUpperLayer(Layer* p_layer) {  };

  virtual void sendData(OCTETSTRING& data, Params& params) { loggers::get_instance().log("Layer::sendData"); };
  virtual void receiveData(OCTETSTRING& data, Params& params) { loggers::get_instance().log("Layer::receiveData"); }
  virtual OCTETSTRING trigger_ac_event(OCTETSTRING& data, Params& params) { loggers::get_instance().log("Layer::trigger_ac_event"); return int2oct(0, 2); }

  inline const std::string& to_string() const { return type; };

protected:
  inline void toAllLayers(std::vector<Layer*>&layers, OCTETSTRING& data, Params& params) {
    //loggers::get_instance().log(">>> Layer::toAllLayer: %d", layers.size());
    for (std::vector<Layer*>::const_iterator it = layers.cbegin(); it != layers.cend(); ++it) {
      Layer * p = *it;
      p->receiveData(data, params); // FIXME BUG I 
    } // End of 'for' statement
  };
  inline void receiveToAllLayers(OCTETSTRING& data, Params& params) {
    //loggers::get_instance().log(">>> Layer::receiveToAllLayers: %d", upperLayers.size());
    for (std::vector<Layer*>::const_iterator it = upperLayers.cbegin(); it != upperLayers.cend(); ++it) {
      Layer * p = *it;
      //loggers::get_instance().log("Layer::receiveToAllLayers: call Layer::receiveData for %s", p->to_string().c_str()); 
      p->receiveData(data, params);
    } // End of 'for' statement
  };
  inline void sendToAllLayers(OCTETSTRING& data, Params& params)  {
    //loggers::get_instance().log(">>> Layer::sendToAllLayers: %d", lowerLayers.size());
    for (std::vector<Layer*>::const_iterator it = lowerLayers.cbegin(); it != lowerLayers.cend(); ++it) {
      Layer * p = *it;
      //loggers::get_instance().log("Layer::sendToAllLayers: call Layer::sendData for %s", p->to_string().c_str()); 
      p->sendData(data, params);
    } // End of 'for' statement
  };
}; // End of class Layer


template <typename TPort> class TLayer : public Layer {
  typedef std::vector<TPort*> TPortList;
  typedef typename std::vector<TPort*>::iterator TPortListIterator;

  TPortList upperPorts;
  
public:
  TLayer() : Layer(), upperPorts() { loggers::get_instance().log("TLayer::TLayer (D)"); };
  TLayer(const std::string& p_type) : Layer(p_type), upperPorts() { loggers::get_instance().log("TLayer::TLayer"); };
  void addUpperPort(TPort * p_port) { upperPorts.push_back(p_port); };
  void removeUpperPort(TPort*);

protected:
  template <typename TMessage>
  inline void toAllUpperPorts(const TMessage& m, const Params& param) {
    //loggers::get_instance().log(">>> TLayer::toAllUpperPorts: %d", upperPorts.size());
    for(TPortListIterator it=upperPorts.begin(); it<upperPorts.end(); ++it){
      (*it)->receiveMsg(m, param);
    }
  }
};


class LayerFactory {
public:
  LayerFactory() {};
  virtual Layer * createLayer(const std::string & type, const std::string & param) = 0;
};

class LayerStackBuilder {
  typedef std::map<std::string, LayerFactory*> LayerFactoryMap;

  static LayerStackBuilder * _instance;
  std::map<std::string, LayerFactory*> _fs;
private:
  LayerStackBuilder(); // can not be created manually
public:
  static LayerStackBuilder * GetInstance();
  static void RegisterLayerFactory(const std::string & type, LayerFactory * f);

public:
  void registerLayerFactory(const std::string & type, LayerFactory * f);
  Layer* createLayerStack(const char*);
};

#endif
