/*!
 * \file      Layer.hh
 * \brief     Header file for ITS abstract protocol layer definition.
 * \author    ETSI STF525
 * \copyright ETSI Copyright Notification
 *            No part may be reproduced except as authorized by written permission.
 *            The copyright and the foregoing restriction extend to reproduction in all media.
 *            All rights reserved.
 * \version   0.1
 */
#pragma once

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

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

class OCTETSTRING; //! Declare TITAN class
class BITSTRING;   //! Declare TITAN class
class CHARSTRING;  //! Declare TITAN class
class INTEGER;     //! Declare TITAN class

/*!
 * \class Layer
 * \brief  This class provides basic description of an ITS protocol layer
 */
class Layer {
  std::vector<Layer*> upperLayers; //! List of the upper protocol layers
  std::vector<Layer*> lowerLayers; //! List of the lower protocol layers

protected:
  std::string type; //! Type description, it indicates the protocol type (e.g. CAM, DENM, GN, ETH, PCAP...)

public:
  /*!
   * \brief Default constructor
   *        Create a new instance of the Layer class
   */
  Layer() : upperLayers(), lowerLayers(), type(std::string("")) { loggers::get_instance().log("Layer::Layer (D)"); };

  /*!
   * \brief Specialized constructor
   *        Create a new instance of the Layer class with its type description
   * \remark This constructor is called by the Layer factory
   * \see LayerFactory
   */
  Layer(const std::string& p_type) : upperLayers(), lowerLayers(), type(std::string(p_type.begin(), p_type.end())) { loggers::get_instance().log("Layer::Layer"); };

  /*!
   * \brief Default destructor
   */
  virtual ~Layer() {
    loggers::get_instance().log("Layer::~Layer");
    // Double linked list, only remove layers in lowerLayers from the lowest one
    std::for_each(lowerLayers.rbegin(), lowerLayers.rend(), [](Layer* it) { delete it; } );
    lowerLayers.clear();
    upperLayers.clear();
  };

  /*!
   * \fn void deleteLayer();
   * \brief Delete this layer
   */
  void deleteLayer() { loggers::get_instance().log("Layer::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);
    };
  };

  /*!
   * \fn void removeUpperLayer(Layer* p_layer);
   * \brief Remove the specified upper layer protocol from the list of the upper layer
   * \param[in] p_layer The layer protocol to be removed
   */
  void removeUpperLayer(Layer* p_layer) {  };

  /*!
   * \virtual
   * \fn void sendData(OCTETSTRING& data, Params& params);
   * \brief Send bytes formated data to the lower layers
   * \param[in] p_data The data to be sent
   * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters
   */
  virtual void sendData(OCTETSTRING& p_data, Params& p_params) { loggers::get_instance().log("Layer::sendData"); };

  /*!
   * \virtual
   * \fn void receiveData(OCTETSTRING& data, Params& params);
   * \brief Receive bytes formated data from the lower layers
   * \param[in] p_data The bytes formated data received
   * \param[in] p_params Some lower layers parameters values when data was received
   */
  virtual void receiveData(OCTETSTRING& p_data, Params& p_params) { loggers::get_instance().log("Layer::receiveData"); }

  /*!
   * \virtual
   * \fn OCTETSTRING trigger_ac_event(OCTETSTRING& data, Params& params);
   * \brief TODO
   * \param[in] p_data
   * \param[in] p_params
   */
  virtual OCTETSTRING trigger_ac_event(OCTETSTRING& p_data, Params& p_params) { loggers::get_instance().log("Layer::trigger_ac_event"); return int2oct(0, 2); }

  /*!
   * \inline
   * \fn const std::string& to_string();
   * \brief Remove the specified upper layer protocol from the list of the upper layer
   * \param[in] The layer protocol to be removed
   */
  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

