#include <stdexcept>
#include <regex>
#include <string>

#include "lost_codec_factory.hh"

#include "loggers.hh"

#include "LibHttp_XmlMessageBodyTypes.hh"

#include "urn_ietf_params_xml_ns_lost1.hh"

int lost_codec::encode (const LibHttp__XmlMessageBodyTypes::XmlBody& msg, OCTETSTRING& data)
{
  loggers::get_instance().log_msg(">>> lost_codec::encode: ", (const Base_Type&)msg);

  TTCN_EncDec::clear_error();
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
  TTCN_Buffer encoding_buffer;

  CHARSTRING h("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
  encoding_buffer.put_s(h.lengthof(), (const unsigned char*)static_cast<const char*>(h));
  if (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_findServiceRequest)) {
    const urn__ietf__params__xml__ns__lost1::FindService& find_service = msg.findServiceRequest();
    loggers::get_instance().log_msg("lost_codec::encode: Process FindService", (const Base_Type&)find_service);
    find_service.encode(urn__ietf__params__xml__ns__lost1::FindService_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else  if (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_listServices)) {
    const urn__ietf__params__xml__ns__lost1::ListServices& list_services = msg.listServices();
    loggers::get_instance().log_msg("lost_codec::encode: Process ListServices", (const Base_Type&)list_services);
    list_services.encode(urn__ietf__params__xml__ns__lost1::ListServices_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else  if (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_listServicesByLocation)) {
    const urn__ietf__params__xml__ns__lost1::ListServicesByLocation& list_services_by_location = msg.listServicesByLocation();
    loggers::get_instance().log_msg("lost_codec::encode: Process ListServicesByLocation", (const Base_Type&)list_services_by_location);
    list_services_by_location.encode(urn__ietf__params__xml__ns__lost1::ListServicesByLocation_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else if  (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_findServiceResponse)) {
    const urn__ietf__params__xml__ns__lost1::FindServiceResponse& find_service_response = msg.findServiceResponse();
    loggers::get_instance().log_msg("lost_codec::encode: Process FindServiceResponse", (const Base_Type&)find_service_response);
    find_service_response.encode(urn__ietf__params__xml__ns__lost1::FindServiceResponse_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else if  (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_listServicesResponse)) {
    const urn__ietf__params__xml__ns__lost1::ListServicesResponse& list_services_response = msg.listServicesResponse();
    loggers::get_instance().log_msg("lost_codec::encode: Process ListServicesResponse", (const Base_Type&)list_services_response);
    list_services_response.encode(urn__ietf__params__xml__ns__lost1::ListServicesResponse_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else if  (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_listServicesByLocationResponse)) {
    const urn__ietf__params__xml__ns__lost1::ListServicesByLocationResponse& list_services_by_location_response = msg.listServicesByLocationResponse();
    loggers::get_instance().log_msg("lost_codec::encode: Process ListServicesByLocationResponse", (const Base_Type&)list_services_by_location_response);
    list_services_by_location_response.encode(urn__ietf__params__xml__ns__lost1::ListServicesByLocationResponse_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else if  (msg.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_redirect)) {
    const urn__ietf__params__xml__ns__lost1::Redirect& redirect = msg.redirect();
    loggers::get_instance().log_msg("lost_codec::encode: Process Redirect", (const Base_Type&)redirect);
    redirect.encode(urn__ietf__params__xml__ns__lost1::Redirect_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
  } else {
    loggers::get_instance().warning("lost_codec::encode: Unsupported variant");
    return -1;
  }
  
  // Work-around to be removed when fixed in urn_ietf..._lost.ttcn
  std::string str((const char*)encoding_buffer.get_data(), (const char*)(encoding_buffer.get_data() + encoding_buffer.get_len()));
  loggers::get_instance().log("lost_codec::encode: Before work-around: %s", str.c_str());
  std::size_t it = str.find("pointType");
  if (it != std::string::npos) {
    str = str.substr(0, it) + "Point" + str.substr(it + 9);
    it = str.find("pointType");
    str = str.substr(0, it) + "Point" + str.substr(it + 9);
  }
  it = str.find("circleType");
  if (it != std::string::npos) {
    str = str.substr(0, it) + "Circle" + str.substr(it + 10);
    it = str.find("circleType");
    str = str.substr(0, it) + "Circle" + str.substr(it + 10);
  }
  
  data = OCTETSTRING(str.length(), (const unsigned char*)str.c_str());
  //data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data());
  loggers::get_instance().log_msg("lost_codec::encode: After encoding: ", data);

  loggers::get_instance().log("<<< lost_codec::encode");
  return 0;
}

int lost_codec::decode (const OCTETSTRING& p_data, LibHttp__XmlMessageBodyTypes::XmlBody& msg, params* p_params)
{
  loggers::get_instance().log_msg(">>> lost_codec::decode: p_data=", p_data);

  // Sanity checks
  params::const_iterator it;
  if (p_params == nullptr) {
    loggers::get_instance().warning("lost_codec::decode: Failed to access p_params (null pointer)");
    return -1;
  } else {
    it = p_params->find("decode_str");
    if (it == p_params->cend()) {
      loggers::get_instance().warning("lost_codec::decode: Failed to access p_params item (decode_str)");
      return -1;
    }
  }
  
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
  TTCN_EncDec::clear_error();
  TTCN_Buffer decoding_buffer(p_data);
  
  if ((it->second.find("<findServiceResponse") != std::string::npos) || (it->second.find(":findServiceResponse") != std::string::npos)) {
    loggers::get_instance().warning("lost_codec::decode: Process FindServiceResponse");
    urn__ietf__params__xml__ns__lost1::FindServiceResponse find_service_response;
    find_service_response.decode(urn__ietf__params__xml__ns__lost1::FindServiceResponse_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.findServiceResponse() = find_service_response;
  } else if ((it->second.find("<listServicesResponse") != std::string::npos) || (it->second.find(":listServicesResponse") != std::string::npos)) {
    loggers::get_instance().warning("lost_codec::decode: Process ListServicesResponse");
    urn__ietf__params__xml__ns__lost1::ListServicesResponse list_services_response;
    list_services_response.decode(urn__ietf__params__xml__ns__lost1::ListServicesResponse_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.listServicesResponse() = list_services_response;
  } else if ((it->second.find("<listServicesByLocationResponse") != std::string::npos) || (it->second.find(":listServicesByLocationResponse") != std::string::npos)) {
    loggers::get_instance().warning("lost_codec::decode: Process ListServicesByLocationResponse");
    urn__ietf__params__xml__ns__lost1::ListServicesByLocationResponse list_services_by_location_response;
    list_services_by_location_response.decode(urn__ietf__params__xml__ns__lost1::ListServicesByLocationResponse_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.listServicesByLocationResponse() = list_services_by_location_response;
} else if ((it->second.find("<redirect") != std::string::npos) || (it->second.find(":redirect") != std::string::npos)) {
    loggers::get_instance().warning("lost_codec::decode: Process Redirect");
    urn__ietf__params__xml__ns__lost1::Redirect redirect;
    redirect.decode(urn__ietf__params__xml__ns__lost1::Redirect_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.redirect() = redirect;    
 } else if ((it->second.find("<findService") != std::string::npos) || (it->second.find(":findService") != std::string::npos)) {
    urn__ietf__params__xml__ns__lost1::FindService find_service;
    find_service.decode(urn__ietf__params__xml__ns__lost1::FindService_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.findServiceRequest() = find_service;
  } else if ((it->second.find("<error") != std::string::npos) || (it->second.find(":error") != std::string::npos)) {
    urn__ietf__params__xml__ns__lost1::ExceptionContainer exception;
    exception.decode(urn__ietf__params__xml__ns__lost1::Errors_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.exceptionContainer() = exception;
  } else {
    loggers::get_instance().warning("lost_codec::decode: Unsupported variant");
    return -1;
  }
  
  loggers::get_instance().log_msg("<<< lost_codec::decode: ", (const Base_Type&)msg);
  return 0;
}

lost_codec_factory lost_codec_factory::_f;
