#include "LibSip_SIPTypesAndValues.hh"
#include "LibSip_Common.hh"

#include "sip_codec_request.hh"
#include "loggers.hh"

#include "converter.hh"

#ifdef WIN32
#undef osip_malloc
#undef osip_free
#define osip_malloc(S) malloc(S)
#define osip_free(P) { if (P!=NULL) { free(P);} }
#endif

int sip_codec_request::encode (const LibSip__SIPTypesAndValues::Request& msg, OCTETSTRING& data)
{
  loggers::get_instance().log_msg(">>> sip_codec_request::encode: ", (const Base_Type&)msg);
  
  TRACE_INITIALIZE(TRACE_LEVEL7, NULL);
  OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, ">>> sip_codec_request::encode\n"));
  
  osip_message_t* sip_message = nullptr; // TODO Use smart pointer
  ::parser_init();
  int result = ::osip_message_init(&sip_message);
  if (result != 0) {
    loggers::get_instance().warning("sip_codec_request::encode: Failed to initialise internal data structures");
    return -1;
  }

  if (encode_request(msg, sip_message) == -1) {
    loggers::get_instance().warning("sip_codec_request::encode: Failed to encode Request");
    ::osip_message_free(sip_message);
    sip_message = nullptr;
    return -1;
  }
  
  char* buffer = nullptr;
  size_t length = 0;
  result = ::osip_message_to_str(sip_message, &buffer, &length);
  if (result != 0) {
    loggers::get_instance().warning("sip_codec_request::encode: Failed to encode data structures");
    ::osip_message_free(sip_message);
    return -1;
  }
  loggers::get_instance().log("sip_codec_request::encode: Message:%s", buffer);
  data = OCTETSTRING(length, (const unsigned char*)buffer);
  osip_free(buffer);
  ::osip_message_free(sip_message);
  
  loggers::get_instance().log_msg("<<< sip_codec_request::encode: data=", data);
  return result;
}

int sip_codec_request::decode (const OCTETSTRING& data, LibSip__SIPTypesAndValues::Request& msg, params* p_params)
{
  loggers::get_instance().log_msg(">>> sip_codec_request::decode: data=", data);

  TRACE_INITIALIZE(TRACE_LEVEL7, NULL);
  OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, ">>> sip_codec_request::decode\n"));
  
  // Sanity checks
  if (data.lengthof() == 0) {
    loggers::get_instance().warning("sip_codec_request::decode: Wrong parameters");
    return -1;
  }

  osip_message_t* sip_message;
  ::parser_init();
  int result = ::osip_message_init(&sip_message);
  if (result != 0) {
    loggers::get_instance().warning("sip_codec_request::decode: Failed to initialise internal data structures");
    return -1;
  }
  result = ::osip_message_parse(sip_message, (const char*)static_cast<const unsigned char*>(data), data.lengthof());
  if (result != 0) {
    loggers::get_instance().warning("sip_codec_request::decode: Failed to parse SIP message");
    ::osip_message_free(sip_message);
    return -1;
  }

  // Fill RequestLine
  decode_request_line(sip_message, msg);

  // Fill Headers
  decode_request_headers(sip_message, msg);

  // Fill MessageBody
  decode_message_body(sip_message, msg.messageBody());
  
  // Fill Payload
  decode_payload(sip_message, msg);
  
  ::osip_message_free(sip_message);
  loggers::get_instance().log_msg("sip_codec_request::decode: ", msg);
  
  return 0;
}

int sip_codec_request::encode_request(const LibSip__SIPTypesAndValues::Request& p_request, osip_message_t* p_sip_message)
{
  loggers::get_instance().log(">>> sip_codec_request::encode_request");
  
  encode_request_line(p_request.requestLine(), p_sip_message);
  
  encode_request_headers(p_request.msgHeader(), p_sip_message);
  
  if (p_request.messageBody().is_present()) {
    const LibSip__MessageBodyTypes::MessageBody& m = static_cast<const LibSip__MessageBodyTypes::MessageBody&>(*p_request.messageBody().get_opt_value());
    encode_message_body(m, p_sip_message);
  }
  
  if (p_request.payload().is_present()) {
    const LibSip__SIPTypesAndValues::Payload& p = static_cast<const LibSip__SIPTypesAndValues::Payload&>(*p_request.payload().get_opt_value());
    encode_request_payload(p, p_sip_message);
  }

  return 0;
} // End of method encode_request

int sip_codec_request::encode_request_line(const LibSip__SIPTypesAndValues::RequestLine& p_request_line, osip_message_t* p_sip_message)
{
  loggers::get_instance().log_msg(">>> sip_codec_request::encode_request_line", p_request_line);

  // Sip method
  std::string str(LibSip__SIPTypesAndValues::Method::enum_to_str(p_request_line.method()));
  char *p = (char*)osip_malloc(str.length() - 2 + 1); // Will be freed by osip_message_free
  ::strcpy(p, str.substr(0, str.length() - 2).c_str()); // Remove _E
  ::osip_message_set_method(p_sip_message, p);

  // Sip uri
  osip_uri_t *uri;
  if (encode_sip_url(p_request_line.requestUri(), &uri) == -1) {
    loggers::get_instance().warning("sip_codec_request::encode_request_line: Faile to encode SipUrl");
    return -1;
  }
  ::osip_message_set_uri(p_sip_message, uri);
  // FIXME: Add parameter to support udp/tcp/tls transport
  //::osip_uri_set_transport_tcp(uri);
  ::osip_uri_set_transport_tls(uri);

  // SIP version
  p = (char*)osip_malloc(p_request_line.sipVersion().lengthof() + 1); // Will be freed by osip_message_free
  ::strcpy(p, (char*)static_cast<const char*>(p_request_line.sipVersion()));
  ::osip_message_set_version(p_sip_message, p);

  return 0;
} // End of method encode_request_line

int sip_codec_request::encode_request_headers(const LibSip__SIPTypesAndValues::MessageHeader& p_msg_header, osip_message_t* p_sip_message)
{ // TODO Rename to encode_headers and call sip_codec_headers::encode_headers
  loggers::get_instance().log_msg(">>> sip_codec_request::encode_request_headers: ", p_msg_header);

  if (encode_headers(p_msg_header, p_sip_message) == -1) {
    loggers::get_instance().warning("sip_codec_request::encode_request_headers: Failed to encode headers");
    return -1;
  }
  
  return 0;
} // End of method encode_request_headers

int sip_codec_request::encode_request_payload(const LibSip__SIPTypesAndValues::Payload& p_payload, osip_message_t* p_sip_message)
{
  loggers::get_instance().log(">>> sip_codec_request::encode_request_payload");

  return 0;
} // End of method encode_request_payload

void sip_codec_request::decode_request_headers(const osip_message_t* p_sip_message, LibSip__SIPTypesAndValues::Request& p_request)
{ // TODO rename into decode_headers and call sip_code_headers::decode_headers
  loggers::get_instance().log(">>> sip_codec_request::decode_request_headers");
  
  LibSip__SIPTypesAndValues::MessageHeader& headers = p_request.msgHeader();
  decode_headers(p_sip_message, headers);
  if (!headers.is_bound()) {
    loggers::get_instance().warning("sip_codec_request::decode_request_headers: Failed to decode headers");
    return;
  }
  
  loggers::get_instance().log_msg("<<< sip_codec_request::decode_request_headers: ", p_request);
} // End of method decode_request_headers

void sip_codec_request::decode_payload(const osip_message_t* p_sip_message, LibSip__SIPTypesAndValues::Request& p_request)
{
  loggers::get_instance().log(">>> sip_codec_request::decode_payload");

  p_request.payload().set_to_omit();
  
} // End of method decode_payload

void sip_codec_request::decode_request_line(const osip_message_t* p_sip_message, LibSip__SIPTypesAndValues::Request& p_request)
{
  loggers::get_instance().log(">>> sip_codec_request::decode_request_line");
  
  LibSip__SIPTypesAndValues::RequestLine request_line;
  std::string str(::osip_message_get_method(p_sip_message));
  str += "_E";
  request_line.method() = LibSip__SIPTypesAndValues::Method(LibSip__SIPTypesAndValues::Method::str_to_enum(str.c_str()));
  loggers::get_instance().log_msg("sip_codec_request::decode_request_line: Method: ", request_line.method());
  LibSip__SIPTypesAndValues::SipUrl uri;
  decode_uri(uri, ::osip_message_get_uri(p_sip_message));
  loggers::get_instance().log_msg("sip_codec_request::decode_request_line: SipUrl: ", uri);
  request_line.requestUri() = uri;
  request_line.sipVersion() = CHARSTRING(::osip_message_get_version(p_sip_message));
  p_request.requestLine() = request_line;
  // FIXME To be continued

  loggers::get_instance().log_msg("<<< sip_codec_request::decode_request_line: ", p_request);
}

