///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2023 Ericsson Telecom AB
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v2.0
// which accompanies this distribution, and is available at
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
///////////////////////////////////////////////////////////////////////////////
//
//  File:               GTPU_EncDec.cc
//  Rev:                R2A
//  Prodnr:             CNL 113 843
//  Contact:            http://ttcn.ericsson.se
//  Reference:          3GPP TS 29.060 v13.5.0

#include "GTPU_Types.hh"

namespace GTPU__Types {

// find the length of the optional part and decode optional part into OPT_PART   
int find_optpart_length(const unsigned char * opt_part_ptr,GTPU__Header__optional__part& OPT_PART)// pointer to opt part start
{
  int opt_part_length = 4; // mandatory minimum length of opt_part  
  OPT_PART.sequenceNumber() = OCTETSTRING(2,opt_part_ptr);
  OPT_PART.npduNumber() = OCTETSTRING(1,opt_part_ptr+2);
  OPT_PART.nextExtHeader() = OCTETSTRING(1,opt_part_ptr+3);
  OPT_PART.gTPU__extensionHeader__List() = OMIT_VALUE; 
   
      int i = 0;    
      bool opt_part_end = false;
      while(!opt_part_end) 
      {                  
        if (opt_part_ptr[opt_part_length-1] != 0x00) // 0x00 means end of optional part
            {
             unsigned char lengthfield = opt_part_ptr[opt_part_length];
             
             OPT_PART.gTPU__extensionHeader__List()()[i].lengthfield() = lengthfield;
             OPT_PART.gTPU__extensionHeader__List()()[i].content() = 
               OCTETSTRING(4*lengthfield-2,opt_part_ptr + opt_part_length +1);
             OPT_PART.gTPU__extensionHeader__List()()[i].nextExtHeader() = 
               OCTETSTRING(1,opt_part_ptr + opt_part_length + 4*lengthfield - 1);
                   
             opt_part_length = opt_part_length + 4*lengthfield;
             i++;
            }
        else            
            {opt_part_end = true;}                         
      }
       
  return  opt_part_length;
}

//////////////////////////////////
// Decoding function for GTPC__DialoguePDU
//////////////////////////////////
PDU__GTPU dec__PDU__GTPU(const OCTETSTRING& udp__pdu)
{
  TTCN_Buffer buf;
  PDU__GTPU pdu;  
       
      const unsigned char *gtpu_message = (const unsigned char *) udp__pdu;
       
      int opt_part_length = 0;
      if  ( gtpu_message[0] & 0x07 ) // opt_part is present 
      { 
         GTPU__Header__optional__part    OPT_PART;
         
         // find the length of the optional part and decode optional part into OPT_PART                       
         opt_part_length = find_optpart_length(gtpu_message+8,OPT_PART); 
         if(  ((gtpu_message[2] << 8) + gtpu_message[3] - opt_part_length) < 0  )
           {TTCN_error("Decoding error, lengthf field is shorter that decoded length of opt_part");};         
                                                             
         // build PDU without optional part 
         unsigned int gtpu_IEs_length = (gtpu_message[2] << 8) + gtpu_message[3] - opt_part_length;
         unsigned char gtpuBuf[8 + gtpu_IEs_length];
         memcpy(gtpuBuf,gtpu_message,8);
         memcpy(gtpuBuf+8,gtpu_message+8+opt_part_length,gtpu_IEs_length);
         
         // substitute dummy bits (indicating there is no optional part)
         gtpuBuf[0] = gtpuBuf[0] & 0xf8;
         
         // substitute dummy length (not including optional part)
         gtpuBuf[2] = (gtpu_IEs_length & 0xff00) >> 8;
         gtpuBuf[3] =  gtpu_IEs_length & 0xff;
         
         // RAW decoding
         buf.put_s(8 + gtpu_IEs_length,gtpuBuf);        
         pdu.decode(PDU__GTPU_descr_, buf, TTCN_EncDec::CT_RAW);
         buf.clear(); 
         
         // put back the original values               
         unsigned char pn = gtpu_message[0] & 0x01;
         pdu.pn__bit() = BITSTRING(1,&pn);
    
         unsigned char s = (gtpu_message[0] & 0x02) >> 1;
         pdu.s__bit() =  BITSTRING(1,&s);
      
         unsigned char e = (gtpu_message[0] & 0x04) >> 2;
         pdu.e__bit() = BITSTRING(1,&e);
         
         pdu.lengthf() = (gtpu_message[2] << 8) + gtpu_message[3];
         
         pdu.opt__part() = OPT_PART;
                
         return pdu;                 
      }   
      else // opt_part is not present 
      {
         buf.put_os(udp__pdu);
         pdu.decode(PDU__GTPU_descr_, buf, TTCN_EncDec::CT_RAW);
         buf.clear(); 
         return pdu;             
      }                  
}  // end of function


}//namespace
