/*!
 * \file      ng_nas_dl_codec.cc
 * \brief     CC file for NG_NAS protocol codec.
 * \author    ETSI TTF041
 * \copyright ETSI Copyright Notification
 *            No part may be reproduced except as authorized by written permission.
 *            The copyright and the foregoing restriction extend to reproduction in all media.
 *            All rights reserved.
 * \version   0.1
 */

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

//#include "ng_nas_dl_codec_factory.hh"

#include "loggers.hh"

#include "NG_NAS_TypeDefs.hh"
#include "NG_NAS_MsgContainers.hh"
#include "ng_nas_dl_codec.hh"

class protocolDiscriminator{
    protocolDiscriminator();
    ~protocolDiscriminator();

int enc(NG__NAS__TypeDefs::ExtdProtocolDiscriminator &p){return 100;} 

};

int ng_nas_dl_codec::encode (const NG__NAS__MsgContainers::NG__NAS__DL__Message__Type& body, OCTETSTRING& data)
{
  loggers::get_instance().log_msg(">>> ng_nas_dl_codec::encode: ", (const Base_Type&)body);

  TTCN_EncDec::clear_error();
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
  TTCN_Buffer encoding_buffer;
  int level=0;
  if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_authentication__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_authentication__Request)) {
    loggers::get_instance().log("ng_nas_dl_codec::encode: Process NG_AUTHENTICATION_REQUEST");
    encode_(level,body.authentication__Request(), *body.authentication__Request().get_descriptor(), encoding_buffer);
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_authentication__Result)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_configuration__Update__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_deregistration__Accept)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_deregistration__RequestMT)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_dl__Nas__Transport)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_gmm__Status)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_gsm__Status)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_identity__Request)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_network__Slice__Specific__Authentication__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_network__Slice__Specific__Authentication__Result)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_notification)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Authentication__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Authentication__Result)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Establishment__Accept)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Establishment__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Modification__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Modification__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Release__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_pdu__Session__Release__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_registration__Accept)) {
    loggers::get_instance().log("ng_nas_dl_codec::encode: Process NG_REGISTRATION_ACCEPT");
    encode_(level,body.registration__Accept(), *body.registration__Accept().get_descriptor(), encoding_buffer);

  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_registration__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_security__Mode__Command)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_security__Protected__Nas__Message)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_service__Accept)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_service__Reject)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_reset__UE__Positioning__Stored__Information)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_nssai__Delete__Request)) {
  }else if (body.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_set__UAI__Request)) {   
  }else{}
  /*if (body.raw().is_present() && body.raw().is_bound()) {
    const CHARSTRING& v = static_cast<const CHARSTRING&>(*body.raw().get_opt_value());
    data = char2oct(v);
  } else {
    TTCN_EncDec::clear_error();
    TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
    TTCN_Buffer encoding_buffer;
    
    const NG__NAS__MsgContainers::NG__NAS__DL__Message__Type& msg = body.msg();
    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(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_locationRequest)) {
      const urn__ietf__params__xml__ns__geopriv__ng_nas::LocationRequestType& location_request = msg.locationRequest();
      loggers::get_instance().log_msg("ng_nas_dl_codec::encode: Process LocationRequestType", (const Base_Type&)location_request);
      location_request.encode(urn__ietf__params__xml__ns__geopriv__ng_nas::LocationRequest_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    } else if  (msg.ischosen(NG__NAS__MsgContainers::NG__NAS__DL__Message__Type::ALT_locationResponse)) {
      const urn__ietf__params__xml__ns__geopriv__ng_nas::LocationResponseType& location_response = msg.locationResponse();
      loggers::get_instance().log_msg("ng_nas_dl_codec::encode: Process LocationResponseType", (const Base_Type&)location_response);
      location_response.encode(urn__ietf__params__xml__ns__geopriv__ng_nas::LocationResponse_descr_, encoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    } else {
      loggers::get_instance().warning("ng_nas_dl_codec::encode: Unsupported variant");
      return -1;
    }
    data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data());
    { // TITAN issue: variant "name as 'device'"; does not work, need to replace "deviceIdenty" by "device"
      std::string s(static_cast<const unsigned char*>(data), data.lengthof() + static_cast<const unsigned char*>(data));
      size_t idx = 0;
      std::string f("deviceIdentity");
      std::string t("device");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement

      // NG112-2021: Apply draft-ietf-geopriv-ng_nas-identity-extensions-06: Use of Device Identity in HTTP-Enabled Location Delivery (HELD) draft-ietf-geopriv-ng_nas-identity-extensions-06
      // FIXME: Add parameter to enable/disable draft-ietf-geopriv-ng_nas-identity-extensions-06 support
      f.assign("xmlns:id");
      t.assign("xmlns");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement
      f.assign("id:device");
      t.assign("device");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement
      f.assign("device:id");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement
      f.assign("xmlns:ng_nas");
      t.assign("xmlns");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement
      f.assign("ng_nas:location");
      t.assign("location");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement
      f.assign("location:ng_nas");
      while ((idx = s.find(f)) != std::string::npos) {
        s.replace(idx, f.length(), t);
        idx += t.length();
      } // End of 'while' statement

      data = OCTETSTRING(s.length(), (const unsigned char*)s.c_str());
    }
  }*/
  data = int2oct(0,1);
  loggers::get_instance().log_msg("ng_nas_dl_codec::encode: After encoding: ", data);
  
  loggers::get_instance().log("<<< ng_nas_dl_codec::encode");
  return -1;
}

int ng_nas_dl_codec::encode_(int level,const Base_Type &type, const TTCN_Typedescriptor_t &field_descriptor, TTCN_Buffer &encoding_buffer) {
  //loggers::get_instance().log(">>> ng_nas_dl_codec::encode_: processing %s/%s", type.get_descriptor()->name, field_descriptor.name);
  // loggers::get_instance().log_msg(">>> geonetworking_codec::encode_: ", type);
  std::string par(level,' ');
  
  if (dynamic_cast<const Record_Type *>(&type) != NULL) {
    const Record_Type &r = (const Record_Type &)type;

    for (int i = 0; i < r.get_count(); i++) {
       //loggers::get_instance().log("ng_nas_dl_codec::encode_: processing - %s/%s - %d (1 ==> use dynamic_cast<const OPTIONAL<...>) - %d", r.fld_name(i),
       //r.fld_descr(i)->name,/* r.get_at(i)->get_descriptor()->name,*/ r.get_at(i)->is_optional(), r.get_at(i)->is_present());
       loggers::get_instance().log("ng_nas_dl_codec::encode_: processing -%s- %s/%s -O %d -P %d", par.c_str(), r.fld_name(i),
       r.fld_descr(i)->name,/* r.get_at(i)->get_descriptor()->name,*/ r.get_at(i)->is_optional(), r.get_at(i)->is_present());
       //if (r.get_at(i)->is_present()) {
       // if (encode_(level++,*r.get_at(i), *r.fld_descr(i), encoding_buffer) == -1) {
       //     //return -1;
       // }
       //}
       std::string s(r.fld_descr(i)->name);
       if(s.compare("@NG_NAS_TypeDefs.ExtdProtocolDiscriminator") ==0){
        //encoding_buffer.put_c(r.get_value(i));
       // const NG__NAS__TypeDefs::ExtdProtocolDiscriminator& pd = <BITSTRING>(*r.get_at(i));
        loggers::get_instance().log_msg("_____________________",*r.get_at(i));
       } 
       if(s.compare("@NAS_CommonTypeDefs.SpareHalfOctet") ==0){
        //protocolDiscriminator pd; pd.enc(r);
        loggers::get_instance().log("_____________________");
       } 
       if(s.compare("@NAS_CommonTypeDefs.SecurityHeaderType") ==0){
        //protocolDiscriminator pd; pd.enc(r);
        loggers::get_instance().log("_____________________");
       }  
       if(s.compare("@NAS_CommonTypeDefs.MessageType") ==0){
        //protocolDiscriminator pd; pd.enc(r);
        loggers::get_instance().log("_____________________");
       }

    } // End of 'for' statement
  }
  return 0;
}

int ng_nas_dl_codec::decode (const OCTETSTRING& p_data, NG__NAS__MsgContainers::NG__NAS__DL__Message__Type& body, params* p_params)
{
  loggers::get_instance().log_msg(">>> ng_nas_dl_codec::decode: p_data=", p_data);

  /*// Sanity checks
  params::const_iterator it;
  if (p_params == nullptr) {
    loggers::get_instance().warning("ng_nas_dl_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("ng_nas_dl_codec::decode: Failed to access p_params item (decode_str)");
      return -1;
    }
  }
  
  loggers::get_instance().log_msg("body: ", body);
  NG__NAS__MsgContainers::NG__NAS__DL__Message__Type& msg = body.msg();
  body.raw().set_to_omit();
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
  TTCN_EncDec::clear_error();
  TTCN_Buffer decoding_buffer(OCTETSTRING(it->second.length(), (const unsigned char*)it->second.c_str()));

  if ((it->second.find("<locationRequest") != std::string::npos) || (it->second.find(":locationRequest") != std::string::npos)) {
    urn__ietf__params__xml__ns__geopriv__ng_nas::LocationRequestType location_request;
    location_request.decode(urn__ietf__params__xml__ns__geopriv__ng_nas::LocationRequest_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.locationRequest() = location_request;
  } else if ((it->second.find("<locationResponse") != std::string::npos) || (it->second.find(":locationResponse") != std::string::npos)) {
    urn__ietf__params__xml__ns__geopriv__ng_nas::LocationResponseType location_response;
    location_response.decode(urn__ietf__params__xml__ns__geopriv__ng_nas::LocationResponse_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.locationResponse() = location_response;
  } else if ((it->second.find("<presence") != std::string::npos) || (it->second.find(":presence") != std::string::npos)) {
    urn__ietf__params__xml__ns__pidf::Presence presence;
    presence.decode(urn__ietf__params__xml__ns__pidf::Presence_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.presence() = presence;
  } else if ((it->second.find("<error") != std::string::npos) || (it->second.find(":error") != std::string::npos)) {
    urn__ietf__params__xml__ns__geopriv__ng_nas::Error error;
    error.decode(urn__ietf__params__xml__ns__geopriv__ng_nas::Error_descr_, decoding_buffer, TTCN_EncDec::CT_XER, XER_EXTENDED);
    msg.errorType() = error;
  } else {
    loggers::get_instance().warning("ng_nas_dl_codec::decode: Unsupported variant");
    return -1;
  }
  
  loggers::get_instance().log_msg("<<< ng_nas_dl_codec::decode: ", (const Base_Type&)msg);*/
  return -1;
}

//ng_nas_dl_codec_factory ng_nas_dl_codec_factory::_f;
