/******************************************************************************
* Copyright (c) 2000-2019 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
*
* Contributors:
*   Gabor Bettesch - initial implementation and initial documentation
******************************************************************************/
//
//  File:     ICMP_EncDec.cc
//  Rev:      R2A
//  Prodnr:   CNL 113 529
///////////////////////////////////////////////////////////////////////////////
#include "ICMP_Types.hh"

namespace ICMP__Types {

TTCN_Module ICMP__EncDec("ICMP_EncDec", __DATE__, __TIME__);

OCTETSTRING f__enc__PDU__ICMP(const PDU__ICMP& pdu)
{
  if (TTCN_Logger::log_this_event(TTCN_DEBUG)) {
    TTCN_Logger::begin_event(TTCN_DEBUG);
    TTCN_Logger::log_event_str("Encoding PDU_ICMP: ");
    pdu.log();
    TTCN_Logger::end_event();
  }
  if (pdu.get_selection() == PDU__ICMP::ALT_unknown)
    return pdu.unknown();

  TTCN_Buffer buf;
  pdu.encode(PDU__ICMP_descr_, buf, TTCN_EncDec::CT_RAW);

  const unsigned char* buf_ptr = buf.get_data();
  size_t datalen = buf.get_len();


//checksum calculation
  unsigned long sum = ( buf_ptr[1] << 8) + buf_ptr[0];

//Skip checksum field

  for (size_t i = 4; i <= datalen - 2; i = i + 2)
    sum += (buf_ptr[i+1] << 8) + buf_ptr[i];

  if (datalen % 2) // datalen is odd
  {
    sum += buf_ptr[datalen-1];
  }

  sum = (sum & 0xFFFF) + (sum >> 16);
  sum = (sum & 0xFFFF) + (sum >> 16);
  sum = ~sum;

  unsigned char ret[4];
  ret[0] = buf_ptr[0];
  ret[1] = buf_ptr[1];
  ret[2] = (sum) & 0x00FF;
  ret[3] = ((sum) & 0xFF00) >> 8;

//put checksum into the checksum field of the message
  OCTETSTRING ret_val = OCTETSTRING(4, ret) + OCTETSTRING(buf.get_len()-4,buf.get_data()+4);

  if (TTCN_Logger::log_this_event(TTCN_DEBUG)) {
    TTCN_Logger::begin_event(TTCN_DEBUG);
    TTCN_Logger::log_event_str("PDU_ICMP after encoding: ");
    ret_val.log();
    TTCN_Logger::end_event();
  }
  return ret_val;
}

}//namespace
