#include "BTPTypes.hh"

#include "BTPLayerFactory.hh"

#include "loggers.hh"

#include "converter.hh"

BTPLayer::BTPLayer(const std::string & p_type, const std::string & param) : TLayer<LibItsBtp__TestSystem::BtpPort>(p_type), _params(), _codec(), _device_mode{false} {
  loggers::get_instance().log(">>> BTPLayer::BTPLayer: %s, %s", to_string().c_str(), param.c_str());
  // Setup parameters
  Params::convert(_params, param);
  
  // Sanity check
  Params::const_iterator it = _params.find(Params::btp_type);
  if (it == _params.cend()) {
    _params[Params::btp_type] = std::string("btpA");
  }
  it = _params.find(Params::btp_destination_port);
  if (it == _params.cend()) {
    _params[Params::btp_destination_port] = std::to_string(2001);
  }
  it = _params.find(Params::btp_info);
  if (it == _params.cend()) {
    _params[Params::btp_info] = std::to_string(0);
  }
  it = _params.find(Params::device_mode);
  if (it != _params.cend()) {
    _device_mode = (1 == converter::get_instance().string_to_int(it->second));
  }
}

void BTPLayer::sendMsg(const LibItsBtp__TestSystem::BtpReq& p, Params& params){
  loggers::get_instance().log(">>> BTPLayer::sendMsg");
  params.log();

  // Encode BTP PDU
  OCTETSTRING data;
  _codec.encode(p.msgOut(), data);
  sendData(data, params);
}

void BTPLayer::sendData(OCTETSTRING& data, Params& params) {
  loggers::get_instance().log_msg(">>> BTPLayer::sendData: ", data);
  params.log(); // TODO To be removed

  if (_device_mode) {
    LibItsBtp__TypesAndValues::BtpHeader header;
    std::string btp_type;
    Params::const_iterator it = params.find(Params::next_header);
    if (it != params.cend()) {
      btp_type = it->second;
    } else {
      btp_type = _params[Params::btp_type];
    }
    if (btp_type.compare("btpA") == 0) {
      header.btpAHeader() = LibItsBtp__TypesAndValues::BtpAHeader(
                                                                  std::stoi(_params[Params::btp_destination_port]),
                                                                  std::stoi(_params[Params::btp_info])
                                                                  );
    } else {
      header.btpBHeader() = LibItsBtp__TypesAndValues::BtpBHeader(
                                                                  std::stoi(_params[Params::btp_destination_port]),
                                                                  std::stoi(_params[Params::btp_info])
                                                                  );
    }
    LibItsBtp__TypesAndValues::BtpPacket p(
                                           header,
                                           data
                                           );
    loggers::get_instance().log_msg("BTPLayer::sendData: ", p);
    
    // Encode BTP PDU
    OCTETSTRING os;
    _codec.encode(p, os);
    data = os;
  }
  
  sendToAllLayers(data, params);
}

void BTPLayer::receiveData(OCTETSTRING& data, Params& params)
{
  loggers::get_instance().log_msg(">>> BTPLayer::receiveData: ", data);
  params.log();  
  // Decode the payload
  LibItsBtp__TestSystem::BtpInd p;
  Params::const_iterator it = params.find(Params::gn_next_header);
  if (it != params.cend()) {
    _codec.set_btp_type((it->second.compare("02") == 0) ? BTPCodec::btpB : BTPCodec::btpA);
  }
  _codec.decode(data, p.msgIn(), &params);
  
  // Pass the BTP raw payload to the upper layers if any
  it = params.find(Params::btp_payload);
  if (it != params.cend()) {
    loggers::get_instance().log("BTPLayer::receiveData: btp_payload=%s", it->second.c_str());
    OCTETSTRING os(str2oct(CHARSTRING(it->second.c_str())));
    receiveToAllLayers(os, params);
  } else {
    loggers::get_instance().warning("BTPLayer::receiveData: No payload to pass to upper layers");
  }
  // Pass it to the ports if any
  //params.log();
  toAllUpperPorts(p, params);
}

BTPLayerFactory BTPLayerFactory::_f;
