#include <string>

#include "LibDiameter_TypesAndValues.hh"

#include "diameter_codec.hh"

#include "loggers.hh"
#include "converter.hh"

template <typename T_type> class OPTIONAL;
class TTCN_Buffer;
class TTCN_EncDec;

diameter_codec::~diameter_codec() {
  loggers::get_instance().log(">>> diameter_codec::~diameter_codec");
  if (_avps.size() != 0) {
    _avps.clear();
  }
}

int diameter_codec::encode_header(const LibDiameter__TypesAndValues::DiameterHeader& p_header, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_header: ", (const Base_Type &)p_header);

  p_encoding_buffer.put_c(p_header.version());

  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.msglen() >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.msglen() >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.msglen()) & 0xFF));

  unsigned char cmdflags = 0x00;
  BITSTRING bit_set = int2bit(1, 1);
  if (p_header.cmdflags().r__bit() == bit_set) {
    cmdflags |= 0x80;
  }
  if (p_header.cmdflags().p__bit() == bit_set) {
    cmdflags |= 0x40;
  }
  if (p_header.cmdflags().e__bit() == bit_set) {
    cmdflags |= 0x20;
  }
  if (p_header.cmdflags().t__bit() == bit_set) {
    cmdflags |= 0x10;
  }
  p_encoding_buffer.put_c(cmdflags);

  int cmdcode = _command_codes[p_header.cmdcode()];
  p_encoding_buffer.put_c(static_cast<const unsigned char>((cmdcode >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((cmdcode >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>(cmdcode & 0xFF));

  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.applid() >> 24) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.applid() >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.applid() >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>(p_header.applid() & 0xFF));

  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.hopid() >> 24) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.hopid() >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.hopid() >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>(p_header.hopid() & 0xFF));

  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.endid() >> 24) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.endid() >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>((p_header.endid() >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const unsigned char>(p_header.endid() & 0xFF));

  _avps.clear(); // unique_ptr class manages deletion of resources

  loggers::get_instance().log_msg("<<< diameter_codec::encode_header: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
} // End of method encode_header

int diameter_codec::encode_session_id(const LibDiameter__Types__Base__AVPs::Session__Id__AVP& p_avp, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_session_id: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));

  // TODO Group

  // Encode AVP header
  int padding = 0;
  if (encode_avp_header(p_avp.aVP__Header(), &padding, p_encoding_buffer) == -1) {
    loggers::get_instance().warning("diameter_codec::encode_session_id: Failed to encode AVP header");
    return -1;
  }

  const OCTETSTRING& os = unichar2oct(p_avp.aVP__Data());
  p_encoding_buffer.put_string(os);

  _avps.insert(
               std::make_pair<const int, std::unique_ptr<avp> >(
                                                                p_avp.aVP__Header().aVP__Code(), 
                                                                std::unique_ptr<avp>(
                                                                                     new avp(
                                                                                             p_avp.aVP__Header().aVP__Code(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().m__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().p__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? static_cast<int>(static_cast<const INTEGER&>(*p_avp.aVP__Header().aVP__vid().get_opt_value())) : 0,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             std::vector<unsigned char>(static_cast<const unsigned char*>(os), static_cast<const unsigned char*>(os) + os.lengthof()),
                                                                                             static_cast<const OCTETSTRING&>(OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()))
                                                                ))));

  if (padding != 0) {
    OCTETSTRING os = int2oct(0, padding);
    p_encoding_buffer.put_string(os);
  }

  loggers::get_instance().log_msg("<<< diameter_codec::encode_session_id: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
}

int diameter_codec::encode_origin_host(const LibDiameter__Types__Base__AVPs::Origin__Host__AVP& p_avp, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_origin_host: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));

  // TODO Group

  // Encode AVP header
  int padding = 0;
  if (encode_avp_header(p_avp.aVP__Header(), &padding, p_encoding_buffer) == -1) {
    loggers::get_instance().warning("diameter_codec::encode_origin_host: Failed to encode AVP header");
    return -1;
  }

  const OCTETSTRING& os = char2oct(p_avp.aVP__Data());
  p_encoding_buffer.put_string(os);

  _avps.insert(
               std::make_pair<const int, std::unique_ptr<avp> >(
                                                                p_avp.aVP__Header().aVP__Code(), 
                                                                std::unique_ptr<avp>(
                                                                                     new avp(
                                                                                             p_avp.aVP__Header().aVP__Code(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().m__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().p__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? static_cast<int>(static_cast<const INTEGER&>(*p_avp.aVP__Header().aVP__vid().get_opt_value())) : 0,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             std::vector<unsigned char>(static_cast<const unsigned char*>(os), static_cast<const unsigned char*>(os) + os.lengthof()),
                                                                                             static_cast<const OCTETSTRING&>(OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()))
                                                                ))));

  if (padding != 0) {
    OCTETSTRING os = int2oct(0, padding);
    p_encoding_buffer.put_string(os);
  }

  loggers::get_instance().log_msg("<<< diameter_codec::encode_origin_host: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
}

int diameter_codec::encode_origin_realm(const LibDiameter__Types__Base__AVPs::Origin__Realm__AVP& p_avp, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_origin_realm: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));

  // TODO Group

  // Encode AVP header
  int padding = 0;
  if (encode_avp_header(p_avp.aVP__Header(), &padding, p_encoding_buffer) == -1) {
    loggers::get_instance().warning("diameter_codec::encode_origin_realm: Failed to encode AVP header");
    return -1;
  }

  const OCTETSTRING& os = char2oct(p_avp.aVP__Data());
  p_encoding_buffer.put_string(os);

  _avps.insert(
               std::make_pair<const int, std::unique_ptr<avp> >(
                                                                p_avp.aVP__Header().aVP__Code(), 
                                                                std::unique_ptr<avp>(
                                                                                     new avp(
                                                                                             p_avp.aVP__Header().aVP__Code(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().m__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().p__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? static_cast<int>(static_cast<const INTEGER&>(*p_avp.aVP__Header().aVP__vid().get_opt_value())) : 0,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             std::vector<unsigned char>(static_cast<const unsigned char*>(os), static_cast<const unsigned char*>(os) + os.lengthof()),
                                                                                             static_cast<const OCTETSTRING&>(OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()))
                                                                ))));

  if (padding != 0) {
    OCTETSTRING os = int2oct(0, padding);
    p_encoding_buffer.put_string(os);
  }

  loggers::get_instance().log_msg("<<< diameter_codec::encode_origin_realm: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
}

int diameter_codec::encode_auth_session_state(const LibDiameter__Types__Base__AVPs::Auth__Session__State__AVP& p_avp, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_auth_session_state: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));

  // TODO Group

  // Encode AVP header
  int padding = 0;
  if (encode_avp_header(p_avp.aVP__Header(), &padding, p_encoding_buffer) == -1) {
    loggers::get_instance().warning("diameter_codec::encode_auth_session_state: Failed to encode AVP header");
    return -1;
  }

  int conv = p_avp.aVP__Data().as_int();
  std::vector<unsigned char> v;
  unsigned char c = static_cast<const unsigned char>((conv >> 24) & 0xFF);
  v.push_back(c);
  p_encoding_buffer.put_c(c);
  c = static_cast<const unsigned char>((conv >> 16) & 0xFF);
  p_encoding_buffer.put_c(c);
  v.push_back(c);
  c = static_cast<const unsigned char>((conv >> 8) & 0xFF);
  p_encoding_buffer.put_c(c);
  v.push_back(c);
  c = static_cast<const unsigned char>(conv & 0xFF);
  p_encoding_buffer.put_c(c);
  v.push_back(c);

  _avps.insert(
               std::make_pair<const int, std::unique_ptr<avp> >(
                                                                p_avp.aVP__Header().aVP__Code(), 
                                                                std::unique_ptr<avp>(
                                                                                     new avp(
                                                                                             p_avp.aVP__Header().aVP__Code(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().m__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             (p_avp.aVP__Header().aVP__flags().p__bit()[0].get_bit()) ? 0x01 : 0x00,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             (p_avp.aVP__Header().aVP__vid().is_present()) ? static_cast<int>(static_cast<const INTEGER&>(*p_avp.aVP__Header().aVP__vid().get_opt_value())) : 0,
                                                                                             p_avp.aVP__Header().aVP__len(),
                                                                                             v,
                                                                                             static_cast<const OCTETSTRING&>(OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()))
                                                                ))));

  if (padding != 0) {
    OCTETSTRING os = int2oct(0, padding);
    p_encoding_buffer.put_string(os);
  }

  loggers::get_instance().log_msg("<<< diameter_codec::encode_auth_session_state: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
}

int diameter_codec::encode_avp_header(const LibDiameter__Types__Base__AVPs::AVP__Header& p_header, int* p_padding, TTCN_Buffer& p_encoding_buffer) {
  loggers::get_instance().log_msg(">>> diameter_codec::encode_avp_header: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));

  int total_length = 4 + 4 + p_header.aVP__len();
  loggers::get_instance().log("diameter_codec::encode_avp_header: total_length: '%d'", total_length);

  if (p_header.aVP__vid().is_present()) {
    total_length += 4;
  }
  int send_length = total_length;
	if ((total_length % 4) != 0) { 
			total_length = (total_length / 4 + 1) * 4;
      *p_padding = total_length - send_length;
  }
  loggers::get_instance().log("diameter_codec::encode_avp_header: send_length: '%d'", send_length);
  loggers::get_instance().log("diameter_codec::encode_avp_header: total_length after padding: '%d'", total_length);
  loggers::get_instance().log("diameter_codec::encode_avp_header: *p_padding: '%d'", *p_padding);

  int conv = p_header.aVP__Code();
  p_encoding_buffer.put_c(static_cast<const char>((conv >> 24) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const char>((conv >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const char>((conv >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const char>(conv & 0xFF));

  unsigned char flags = 0x00;
  if (p_header.aVP__vid().is_present()) {
    flags |= 0x80;
  }
  if (p_header.aVP__flags().m__bit()[0].get_bit()) {
    flags |= 0x40;
    
  }
  if (p_header.aVP__flags().p__bit()[0].get_bit()) {
    flags |= 0x20;
    
  }
  p_encoding_buffer.put_c(flags);

  conv = send_length;
  p_encoding_buffer.put_c(static_cast<const char>((send_length >> 16) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const char>((send_length >> 8) & 0xFF));
  p_encoding_buffer.put_c(static_cast<const char>(send_length & 0xFF));

  if (p_header.aVP__vid().is_present()) {
    conv = static_cast<const INTEGER&>(*p_header.aVP__vid().get_opt_value());
    p_encoding_buffer.put_c(static_cast<const char>((conv >> 24) & 0xFF));
    p_encoding_buffer.put_c(static_cast<const char>((conv >> 16) & 0xFF));
    p_encoding_buffer.put_c(static_cast<const char>((conv >> 8) & 0xFF));
    p_encoding_buffer.put_c(static_cast<const char>(conv & 0xFF));
  }

  loggers::get_instance().log_msg("<<< diameter_codec::encode_auth_session_state: ", OCTETSTRING(p_encoding_buffer.get_len(), p_encoding_buffer.get_data()));
  return 0;
}

int diameter_codec::decode_header(TTCN_Buffer& p_decoding_buffer, LibDiameter__TypesAndValues::DiameterHeader& p_header, const params& p_params) {
  loggers::get_instance().log(">>> diameter_codec::decode_header: '%p'", _start);

  // Sanity check
  if (p_decoding_buffer.get_len() < 20) { // DiameterHeader size
    loggers::get_instance().warning("diameter_codec::decode: Wrong packet length for daimeter message");
    return -1;
  }

  _start = static_cast<const unsigned char*>(p_decoding_buffer.get_data());
  _avps.clear(); // unique_ptr class manages deletion of resources

  p_header.version() = static_cast<int>(*_start++ & 0xFF);
  p_header.msglen() = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));
  // Resize the docoding buffer
  if ((int)p_header.msglen() > p_decoding_buffer.get_len()) {
    loggers::get_instance().warning("diameter_codec::decode: Resize docoding buffer: '%d' to '%d'", p_header.msglen(), p_decoding_buffer.get_len());
    p_header.msglen() = p_decoding_buffer.get_len();
  }
  _end = static_cast<const unsigned char*>(p_decoding_buffer.get_data()) + p_header.msglen();
  loggers::get_instance().log("diameter_codec::decode: _end: '%p' - offset: '%d'", _end, static_cast<int>(_end - _start));
  loggers::get_instance().log_to_hexa("diameter_codec::decode: diameter message:", _start, static_cast<int>(_end - _start));

  p_header.cmdflags().r__bit() = int2bit(static_cast<int>(static_cast<unsigned char>(*_start & 0x80) != 0), 1);
  p_header.cmdflags().p__bit() = int2bit(static_cast<int>(static_cast<unsigned char>(*_start & 0x40) != 0), 1);
  p_header.cmdflags().e__bit() = int2bit(static_cast<int>(static_cast<unsigned char>(*_start & 0x20) != 0), 1);
  p_header.cmdflags().t__bit() = int2bit(static_cast<int>(static_cast<unsigned char>(*_start & 0x10) != 0), 1);
  p_header.cmdflags().reserved__bits() = int2bit(static_cast<int>(*_start++ & 0x0F), 4);

  params::const_iterator it = p_params.find(std::string("Request"));
  boolean is_request = true;
  if (it != p_params.cend()) {
    is_request = it->second.compare("1") == 0;
  }
  p_header.cmdcode() = LibDiameter__TypesAndValues::Command__Code::str_to_enum(
                                                                               command_code_2_enumerated(
                                                                                                         static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF)), 
                                                                                                         static_cast<const boolean>(is_request)
                                                                                                         ).c_str()
                                                                               );

  p_header.applid() = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 24 | static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));

  p_header.hopid() = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 24 | static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));

  p_header.endid() = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 24 | static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));

  loggers::get_instance().log_msg("diameter_codec::decode_header: p_header=", p_header);

  return 0;
} // End of method decode_header

int diameter_codec::decode_avps(TTCN_Buffer& p_decoding_buffer, const LibDiameter__TypesAndValues::DiameterHeader& p_header, const params& p_params) {
  loggers::get_instance().log(">>> diameter_codec::decode_avps: '%p'", _start);

  while (_start < _end) {
    avp* avp_ptr = nullptr;
    if (decode_avp(p_decoding_buffer, p_header, p_params, &avp_ptr) == -1) {
      loggers::get_instance().log("diameter_codec::decode_avps: Failed to decode AVP: current position: '%p' - offset:'%d' - value:'0x%02x'", _start, reinterpret_cast<std::uintptr_t>(_end) - reinterpret_cast<std::uintptr_t>(_start), static_cast<unsigned char>(*_start));
      return -1;
    }
    _avps.insert(std::make_pair<const int, std::unique_ptr<avp> >(avp_ptr->get_code(), std::unique_ptr<avp>(avp_ptr)));
  }   // End of 'while' statemement
  loggers::get_instance().log(">>> diameter_codec::decode_avps: No more AVP to decode: '%p' / '%p'", _start, _end);

  return 0;
} // End of method decode_avps

int diameter_codec::decode_avp(TTCN_Buffer& p_decoding_buffer, const LibDiameter__TypesAndValues::DiameterHeader& p_header, const params& p_params, avp** avp_ptr) {
  loggers::get_instance().log(">>> diameter_codec::decode_avp: '%p'", _start);

  // Sanity checks
  if (static_cast<int>((_end - _start)) < 8) {
    loggers::get_instance().warning("diameter_codec::decode_avp: Not enought bytes for AVP decoding");
    return -1;
  }

  int code = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 24 | static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));
  loggers::get_instance().log("diameter_codec::decode_avp: code:'%d'", code);
  unsigned char vendor_specific = static_cast<unsigned char>(static_cast<unsigned char>(*_start & 0x80) != 0);
  loggers::get_instance().log("diameter_codec::decode_avp: vendor_specific:'0x%x'", vendor_specific);
  unsigned char mandatory = static_cast<unsigned char>(static_cast<unsigned char>(*_start & 0x40) != 0);
  loggers::get_instance().log("diameter_codec::decode_avp: mandatory:'0x%x'", mandatory);
  unsigned char protected_ = static_cast<unsigned char>(static_cast<unsigned char>(*_start++ & 0x20) != 0);
  loggers::get_instance().log("diameter_codec::decode_avp: protected_:'0x%x'", protected_);
  int length = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));
  loggers::get_instance().log("diameter_codec::decode_avp: length:'%d'", length);

  int vendor_id = 0;
  int payload_length = length;
  if (vendor_specific) {
    vendor_id = static_cast<int>(static_cast<int>(*_start++ & 0xFF) << 24 | static_cast<int>(*_start++ & 0xFF) << 16 | static_cast<int>(*_start++ & 0xFF) << 8 | static_cast<int>(*_start++ & 0xFF));
    payload_length -= 12;  
  } else {
    payload_length -= 8;  
  }
  loggers::get_instance().log("diameter_codec::decode_avp: payload_length:'%d'", payload_length);
  std::vector<unsigned char> payload(_start, _start + payload_length);

  // if (length > static_cast<int>(_end - _start)) {
  //   loggers::get_instance().warning("diameter_codec::decode_avp: Resize docoding buffer: '%d' to '%d'", length, static_cast<int>(_end - _start));
  //   length = static_cast<int>(_end - _start);
  // }

  int encoded_length = length;
  if ((encoded_length % 4) !=0) {
    encoded_length = (encoded_length / 4 + 1) * 4;
  } 
  loggers::get_instance().log("diameter_codec::decode_avp: encoded_length:'%d'", encoded_length);

  loggers::get_instance().log("diameter_codec::decode_avp: encoded_length:'%d'", encoded_length - length);
  loggers::get_instance().log("diameter_codec::decode_avp: payload_length + (encoded_length - length):'%d'", payload_length + (encoded_length - length));
  _start = _start + payload_length + (encoded_length - length);
  loggers::get_instance().log("diameter_codec::decode_avp: new _start:'%p'", _start);

  *avp_ptr = new avp(code, vendor_specific, mandatory, protected_, length, vendor_id, encoded_length, payload);

  return 0;
}

const std::unique_ptr<avp>& diameter_codec::get_avp(const int p_code) {
  loggers::get_instance().log(">>> diameter_codec::get_avp: '%d'", p_code);

  if ((_it = _avps.find(p_code)) == _avps.cend()) {
    loggers::get_instance().log("diameter_codec::get_avp: Failed to retrieve AVP '%d'", p_code);
    return _nullptr;
  }

  loggers::get_instance().log("diameter_codec::get_avp: got AVP '%d'", _it->second->get_code());
  return _it->second;
}

std::string diameter_codec::command_code_2_enumerated(const int p_code, const boolean p_request_flag) {

  std::string code_enum;
  if(p_request_flag) {
    switch(p_code){
      case Code_CE: code_enum.assign("CER_E"); break;
      case Code_RA: code_enum.assign("RAR_E"); break;
      case Code_AC: code_enum.assign("ACR_E"); break;
      case Code_AS: code_enum.assign("ASR_E"); break;
      case Code_ST: code_enum.assign("STR_E"); break;
      case Code_DW: code_enum.assign("DWR_E"); break;
      case Code_DP: code_enum.assign("DPR_E"); break;
          //CxDx
      case Code_UA: code_enum.assign("UAR_E"); break;
      case Code_SA: code_enum.assign("SAR_E"); break;
      case Code_LI: code_enum.assign("LIR_E"); break;
      case Code_MA: code_enum.assign("MAR_E"); break;
      case Code_RT: code_enum.assign("RTR_E"); break;
      case Code_PP: code_enum.assign("PPR_E"); break;

      /* Codes on Sh/Dh interface  */
      case Code_UD: code_enum.assign("UDR_E"); break;// UDR User-Data-Request AS->HSS                UDR    (306)
      case Code_PU: code_enum.assign("PUR_E"); break;// PUR Profile-Update-Request AS->HSS           PUR    (307)
      case Code_SN: code_enum.assign("SNR_E"); break;// SNR Subscribe-Notifications-Request AS->HSS  SNR    (308)
      case Code_PN: code_enum.assign("PNR_E"); break;// PNR Profile-Notification-Request HSS->AS     

      //Rx
      case Code_AA: code_enum.assign("AAR_E"); break;
      //4006
      case Code_CC: code_enum.assign("CCR_E"); break;

      //s6a
      case Code_UL: code_enum.assign("ULR_E"); break;  // Update-Location-Request                   ULR    (316)- Ref: TS 129 272 7.2.3-4
      case Code_CL: code_enum.assign("CLR_E"); break;  // Cancel-Location-Request                   CLR    (317)- Ref: TS 129 272 7.2.7-8
      case Code_AI: code_enum.assign("AIR_E"); break;  // Authentication-Information-Request        AIR    (318)- Ref: TS 129 272 7.2.5-6
      case Code_ID: code_enum.assign("IDR_E"); break;  // Insert-Subscriber-Data-Request            IDR    (319)- Ref: TS 129 272 7.2.9-10
      case Code_DS: code_enum.assign("DSR_E"); break;  // Delete-Subscriber-Data-Request            DSR    (320)- Ref: TS 129 272 7.2.11-12
      case Code_PUE: code_enum.assign("PUER_E"); break; // Purge-UE-Request                          PUR    (321)- Ref: TS 129 272 7.2.13-14
      case Code_RS: code_enum.assign("RSR_E"); break;  // Reset-Request                             DSR    (322)- Ref: TS 129 272 7.2.15-16
      case Code_NO: code_enum.assign("NOR_E"); break;  // Notify-Request                            NOR    (323)- Ref: TS 129 272 7.2.17-18
    }
  } else {
    switch(p_code) {
      case Code_CE: code_enum.assign("CEA_E"); break;
      case Code_RA: code_enum.assign("RAA_E"); break;
      case Code_AC: code_enum.assign("ACA_E"); break;
      case Code_AS: code_enum.assign("ASA_E"); break;
      case Code_ST: code_enum.assign("STA_E"); break;
      case Code_DW: code_enum.assign("DWA_E"); break;
      case Code_DP: code_enum.assign("DPA_E"); break;
      //CxDx
      case Code_UA: code_enum.assign("UAA_E"); break;
      case Code_SA: code_enum.assign("SAA_E"); break;
      case Code_LI: code_enum.assign("LIA_E"); break;
      case Code_MA: code_enum.assign("MAA_E"); break;
      case Code_RT: code_enum.assign("RTA_E"); break;
      case Code_PP: code_enum.assign("PPA_E"); break;

      /* Codes on Sh/Dh interface  */
      case Code_UD: code_enum.assign("UDA_E"); break;// UDR User-Data-Request AS->HSS                UDR    (306)
      case Code_PU: code_enum.assign("PUA_E"); break;// PUR Profile-Update-Request AS->HSS           PUR    (307)
      case Code_SN: code_enum.assign("SNA_E"); break;// SNR Subscribe-Notifications-Request AS->HSS  SNR    (308)
      case Code_PN: code_enum.assign("PNA_E"); break;// PNR Profile-Notification-Request HSS->AS 
      //Rx
      case Code_AA: code_enum.assign("AAA_E"); break;
      //4006
      case Code_CC: code_enum.assign("CCA_E"); break;

      //S6a
      case Code_UL: code_enum.assign("ULA_E"); break;  // Update-Location-Request                   ULR    (316)- Ref: TS 129 272 7.2.3-4
      case Code_CL: code_enum.assign("CLA_E"); break;  // Cancel-Location-Request                   CLR    (317)- Ref: TS 129 272 7.2.7-8
      case Code_AI: code_enum.assign("AIA_E"); break;  // Authentication-Information-Request        AIR    (318)- Ref: TS 129 272 7.2.5-6
      case Code_ID: code_enum.assign("IDA_E"); break;  // Insert-Subscriber-Data-Request            IDR    (319)- Ref: TS 129 272 7.2.9-10
      case Code_DS: code_enum.assign("DSA_E"); break;  // Delete-Subscriber-Data-Request            DSR    (320)- Ref: TS 129 272 7.2.11-12
      case Code_PUE: code_enum.assign("PUEA_E"); break; // Purge-UE-Request                          PUR    (321)- Ref: TS 129 272 7.2.13-14
      case Code_RS: code_enum.assign("RSA_E"); break;  // Reset-Request                             DSR    (322)- Ref: TS 129 272 7.2.15-16
      case Code_NO: code_enum.assign("NOA_E"); break;  // Notify-Request                            NOR    (323)- Ref: TS 129 272 7.2.17-1
    }
  } 
  return code_enum;
} // End of method command_code_2_enumerated

const int diameter_codec::_command_codes[] = {
  /* Ref: RFC3588 p.3.1 */

  diameter_codec::Code_CE,  // Capabilities-Exchange-Request   CER  (257)
  diameter_codec::Code_CE,  // Capabilities-Exchange-Answer   CEA  (257)
  diameter_codec::Code_RA,  // Re-Auth-Request         RAR  (258)
  diameter_codec::Code_RA,  // Re-Auth-Answer         RAA  (258)
  /**/  
  diameter_codec::Code_AC,  // Accounting-Request        ACR(271)
  diameter_codec::Code_AC,  // Accounting-Answer        ACA (271)
    /**/
  diameter_codec::Code_AS,  // Abort-Session-Request      ASR (274)
  diameter_codec::Code_AS,  // Abort-Sesion-Answer      ASA (274)
  diameter_codec::Code_ST,  // Session-Termination-Request  STR (275)
  diameter_codec::Code_ST,  // Session-Termination-Answer    STA (275)          
/**/
  diameter_codec::Code_DW,  // Device-Watchdog-Request    DWR (280)
  diameter_codec::Code_DW,  // Device-Watchdog-Answer      DWA (280)
  diameter_codec::Code_DP,  // Disconnect-Peer-Request      DPR (282)
  diameter_codec::Code_DP,  // Disconnect-Peer-Answer      DPA (282) 

/* Codes on Cx */
  diameter_codec::Code_UA,     // User-Authorization-Request  UAR  300
  diameter_codec::Code_UA,     // User-Authorization-Answer  UAA  300
  diameter_codec::Code_SA,     // Server-Assignment-Request  SAR  301
  diameter_codec::Code_SA,     // Server-Assignment-Answer    SAA  301
  diameter_codec::Code_LI,     // Location-Info-Request    LIR  302
  diameter_codec::Code_LI,     // Location-Info-Answer      LIA  302
  diameter_codec::Code_MA,     // Multimedia-Auth-Request    MAR  303
  diameter_codec::Code_MA,     // Multimedia-Auth-Answer    MAA  303
  diameter_codec::Code_RT,    // Registration-Termination-Request  RTR  304
  diameter_codec::Code_RT,    // Registration-Termination-Answer  RTA  304
  diameter_codec::Code_PP,    // Push-Profile-Request      PPR  305
  diameter_codec::Code_PP,    // Push-Profile-Answer      PPA  305  

/* Codes on Sh/Dh interface  */
  diameter_codec::Code_UD,  //  UDR User-Data-Request AS->HSS   306
  diameter_codec::Code_UD,  //  UDA User-Data-Answer HSS->AS  306    
  diameter_codec::Code_PU,   //  PUR Profile-Update-Request AS->HSS  307
  diameter_codec::Code_PU,  //  PUA Profile-Update-Answer HSS->AS  307
  diameter_codec::Code_SN,  //  SNR Subscribe-Notifications-Request AS->HSS  308
  diameter_codec::Code_SN,  //  SNA Subscribe-Notifications-Answer HSS->AS  308
  diameter_codec::Code_PN,  //  PNR Profile-Notification-Request HSS->AS  309
  diameter_codec::Code_PN,  //  PNA Profile-Notification-Answer AS->HSS    309

/* Codes on Rx interface  */
  diameter_codec::Code_AA,  //  AAR Auth-Accounting-Request    265
  diameter_codec::Code_AA,  //  AAA Auth-Accounting-Request   265

/*RFC4006*/
  diameter_codec::Code_CC,  //  CCR Credit-Control-Request    272
  diameter_codec::Code_CC,  //  CCA Credit-Control-Answer     272

    /* Codes on S6a interface - TS 129 272  */
  diameter_codec::Code_UL,  // Update-Location-Request                   ULR    (316)- Ref: TS 129 272 7.2.3
  diameter_codec::Code_UL,  // Update-Location-Answer                ULA    (316)- Ref: TS 129 272 7.2.4
  diameter_codec::Code_CL,  // Cancel-Location-Request                   CLR    (317)- Ref: TS 129 272 7.2.7
  diameter_codec::Code_CL,  // Cancel-Location-Answer                    CLA    (317)- Ref: TS 129 272 7.2.8
  diameter_codec::Code_AI,  // Authentication-Information-Request        AIR    (318)- Ref: TS 129 272 7.2.5
  diameter_codec::Code_AI,  // Authentication-Information-Answer         AIA    (318)- Ref: TS 129 272 7.2.6
  diameter_codec::Code_ID,  // Insert-Subscriber-Data-Request            IDR    (319)- Ref: TS 129 272 7.2.9
  diameter_codec::Code_ID,  // Insert-Subscriber-Data-Answer             IDA    (319)- Ref: TS 129 272 7.2.10
  diameter_codec::Code_DS,  // Delete-Subscriber-Data-Request            DSR    (320)- Ref: TS 129 272 7.2.11
  diameter_codec::Code_DS,  // Delete-Subscriber-Data-Answer             DSA    (320)- Ref: TS 129 272 7.2.12
  diameter_codec::Code_PUE, // Purge-UE-Request                          PUR    (321)- Ref: TS 129 272 7.2.13
  diameter_codec::Code_PUE, // Purge-UE-Answer                           PUA    (321)- Ref: TS 129 272 7.2.14
  diameter_codec::Code_RS,  // Reset-Request                             DSR    (322)- Ref: TS 129 272 7.2.15
  diameter_codec::Code_RS,  // Reset-Answer                              DSA    (322)- Ref: TS 129 272 7.2.16
  diameter_codec::Code_NO,  // Notify-Request                            NOR    (323)- Ref: TS 129 272 7.2.17
  diameter_codec::Code_NO   // Notify-Answer                             NOA    (323)- Ref: TS 129 272 7.2.18    
};
