#include "EthernetLayerFactory.hh"

#include "loggers.hh"

EthernetLayer::EthernetLayer(const std::string & p_type, const std::string & param) : Layer(p_type), _params() {
  loggers::get_instance().log(">>> EthernetLayer::EthernetLayer: %s, %s", to_string().c_str(), param.c_str());
  // Setup parameters
  Params::convert(_params, param);
  Params::const_iterator it = _params.find("mac_bc");
  if (it == _params.cend()) {
    _params.insert(std::pair<std::string, std::string>(std::string("mac_bc"), "FFFFFFFFFFFF"));
  }
  //_params.log();
}

void EthernetLayer::sendData(OCTETSTRING& data, Params& params) {
  loggers::get_instance().log_msg(">>> EthernetLayer::sendData: ", data);

  OCTETSTRING eth;
  // Destination MAC address
  Params::const_iterator it = params.find(Params::mac_dst); // Find in provided parameters, params
  if (it != params.cend()) {
    eth = str2oct(CHARSTRING(it->second.c_str()));
  } else {
    it = _params.find(Params::mac_dst); // Find in layer parameters, _params
    if (it != _params.cend()) {
      eth = str2oct(CHARSTRING(it->second.c_str()));
    } else {
      eth = str2oct(CHARSTRING(_params[Params::mac_bc].c_str()));
    }
  }
  // Source MAC address
  it = params.find(Params::mac_src); // Find in provided parameters, params
  if (it != params.cend()) {
    eth += str2oct(CHARSTRING(it->second.c_str()));
  } else {
    it = _params.find(Params::mac_src); // Find in layer parameters, _params
    if (it != _params.cend()) {
      eth += str2oct(CHARSTRING(it->second.c_str()));
    } else {
      const unsigned char null_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
      eth += OCTETSTRING(sizeof(null_mac), static_cast<const unsigned char *>(null_mac));
    }
  }
  it = _params.find(Params::eth_type); // Find in layer parameters
  if (it != params.cend()) {
    eth += str2oct(CHARSTRING(it->second.c_str()));
  } else {
    const unsigned char proto[] = { 0x89, 0x47 };
    eth += OCTETSTRING(sizeof(proto), static_cast<const unsigned char *>(proto));
  }
  
  eth += data;
  sendToAllLayers(eth, params);
}

void EthernetLayer::receiveData(OCTETSTRING& data, Params& params) {
  loggers::get_instance().log_msg(">>> EthernetLayer::receiveData: ", data);

  // Extract dest MAC Address
  OCTETSTRING dst = OCTETSTRING(6, static_cast<const unsigned char *>(data));
  //loggers::get_instance().log_msg("EthernetLayer::receiveData: dst: ", dst);
  // Extract source MAC Address
  OCTETSTRING src = OCTETSTRING(6, 6 + static_cast<const unsigned char *>(data));
  //loggers::get_instance().log_msg("EthernetLayer::receiveData: src: ", src);
  // Extract ethertype
  OCTETSTRING proto = OCTETSTRING(2, 2 + static_cast<const unsigned char *>(data));
  //loggers::get_instance().log_msg("EthernetLayer::receiveData: proto: ", proto);
  data = OCTETSTRING(data.lengthof() - 14, 14 + static_cast<const unsigned char *>(data));
  // Update params
  CHARSTRING s = oct2str(dst);
  params.insert(std::pair<std::string, std::string>(Params::mac_dst, std::string(static_cast<const char *>(s))));
  s = oct2str(src);
  params.insert(std::pair<std::string, std::string>(Params::mac_src, std::string(static_cast<const char *>(s))));
  //loggers::get_instance().log_msg("EthernetLayer::receiveData: payload for upper layer:", data);
  
  receiveToAllLayers(data, params);
}

EthernetLayerFactory EthernetLayerFactory::_f;
