/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*
                                play_scenario_display.c
                                -------------------
  AUTHOR  : Lionel GAUTHIER
  COMPANY : EURECOM
  EMAIL   : Lionel.Gauthier@eurecom.fr
 */
#include <string.h>
#include <stdio.h>

#include "intertask_interface.h"
#include "platform_types.h"
#include "assertions.h"
#include "s1ap_eNB_decoder.h"
#include "play_scenario.h"
//-----------------------------------------------------------------------------
void et_print_hex_octets(const unsigned char *const byte_stream, const unsigned long int num)
{
  unsigned long octet_index = 0;

  if (byte_stream == NULL) {
    return;
  }

  fprintf(stdout, "+-----+-------------------------------------------------+\n");
  fprintf(stdout, "|     |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |\n");
  fprintf(stdout, "+-----+-------------------------------------------------+\n");

  for (octet_index = 0; octet_index < num; octet_index++) {
    if ((octet_index % 16) == 0) {
      if (octet_index != 0) {
        fprintf(stdout, " |\n");
      }

      fprintf(stdout, " %04lu |", octet_index);
    }

    /*
     * Print every single octet in hexadecimal form
     */
    fprintf(stdout, " %02x", byte_stream[octet_index]);
    /*
     * Align newline and pipes according to the octets in groups of 2
     */
  }

  /*
   * Append enough spaces and put final pipe
   */
  unsigned char index;

  for (index = octet_index; index < 16; ++index) {
    fprintf(stdout, "   ");
  }

  fprintf(stdout, " |\n");
}

//------------------------------------------------------------------------------
void et_display_node(xmlNodePtr node, unsigned int indent)
{
  int i = 0;

  if (node->type == XML_ELEMENT_NODE) {
    xmlChar *path = xmlGetNodePath(node);

    for (i=0; i<indent; i++) {
      printf("  ");
    }

    if (node->children != NULL && node->children->type == XML_TEXT_NODE) {
      xmlChar *content = xmlNodeGetContent(node);
      printf("%s -> %s\n", path, content);
      xmlFree(content);
    } else {
      printf("%s\n", path);
    }

    xmlFree(path);
  }
}
//------------------------------------------------------------------------------
void et_display_tree(xmlNodePtr node, unsigned int indent)
{
  xmlNode *cur_node = NULL;

  for (cur_node = node; cur_node; cur_node = cur_node->next) {
    if (cur_node->type == XML_ELEMENT_NODE) {
      et_display_node(cur_node, indent);
    }

    et_display_tree(cur_node->children, indent++);
  }
}
//------------------------------------------------------------------------------
void et_display_packet_S1AP_data(const et_S1AP_t *const s1ap)
{
  char       *message_string = NULL;

  if (s1ap) {
    message_string = calloc(20000, sizeof(char));
    AssertFatal (NULL != message_string, "ERROR malloc()failed!\n");

    switch(s1ap->pdu.present) {
      case S1AP_S1AP_PDU_PR_initiatingMessage:
        switch(s1ap->pdu.choice.initiatingMessage.procedureCode) {
          case S1AP_ProcedureCode_id_downlinkNASTransport:      s1ap_xer_print_s1ap_downlinknastransport(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_InitialContextSetup:       s1ap_xer_print_s1ap_initialcontextsetuprequest(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_UEContextRelease:          s1ap_xer_print_s1ap_uecontextreleasecommand(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_Paging:                    s1ap_xer_print_s1ap_paging(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_uplinkNASTransport:        s1ap_xer_print_s1ap_uplinknastransport(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_S1Setup:                   s1ap_xer_print_s1ap_s1setuprequest(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_initialUEMessage:          s1ap_xer_print_s1ap_initialuemessage(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_UEContextReleaseRequest:   s1ap_xer_print_s1ap_uecontextreleaserequest(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_UECapabilityInfoIndication:s1ap_xer_print_s1ap_uecapabilityinfoindication(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_NASNonDeliveryIndication:  s1ap_xer_print_s1ap_nasnondeliveryindication_(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          default:
            AssertFatal( 0, "Unknown procedure ID (%d) for initiating message\n",
                         (int)s1ap->pdu.choice.initiatingMessage.procedureCode);
        }

        break;

      case S1AP_S1AP_PDU_PR_successfulOutcome:
        switch(s1ap->pdu.choice.successfulOutcome.procedureCode) {
          case S1AP_ProcedureCode_id_S1Setup:                   s1ap_xer_print_s1ap_s1setupresponse(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_InitialContextSetup:       s1ap_xer_print_s1ap_initialcontextsetupresponse(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_UEContextRelease:          s1ap_xer_print_s1ap_uecontextreleasecomplete(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          default:
            AssertFatal(0, "Unknown procedure ID (%d) for successfull outcome message\n",
                        (int)s1ap->pdu.choice.successfulOutcome.procedureCode);
        }

        break;

      case S1AP_S1AP_PDU_PR_unsuccessfulOutcome:
        switch(s1ap->pdu.choice.unsuccessfulOutcome.procedureCode) {
          case S1AP_ProcedureCode_id_S1Setup:                   s1ap_xer_print_s1ap_s1setupfailure(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          case S1AP_ProcedureCode_id_InitialContextSetup:       s1ap_xer_print_s1ap_initialcontextsetupfailure(s1ap_xer__print2sp, message_string, (S1AP_message *)&s1ap->message); break;

          default:
            et_free_pointer(message_string);
            AssertFatal(0,"Unknown procedure ID (%d) for unsuccessfull outcome message\n",
                        (int)s1ap->pdu.choice.unsuccessfulOutcome.procedureCode);
            break;
        }

        break;

      default:
        AssertFatal(0, "Unknown presence (%d) or not implemented\n", (int)s1ap->pdu.present);
        break;
    }

    fprintf(stdout, "\t\tSCTP.data XML dump:\n%s\n", message_string);
    et_free_pointer(message_string);
  }
}
//------------------------------------------------------------------------------
void et_display_packet_sctp_init(const sctp_inithdr_t *const sctp)
{
  if (sctp) {
    fprintf(stdout, "\t\tSCTP.init.init_tag               : %u\n", sctp->init_tag);
    fprintf(stdout, "\t\tSCTP.init.a_rwnd                 : %u\n", sctp->a_rwnd);
    fprintf(stdout, "\t\tSCTP.init.num_inbound_streams    : %u\n", sctp->num_inbound_streams);
    fprintf(stdout, "\t\tSCTP.init.num_outbound_streams   : %u\n", sctp->num_outbound_streams);
    fprintf(stdout, "\t\tSCTP.init.initial_tsn            : %u\n", sctp->initial_tsn);
  }
}
//------------------------------------------------------------------------------
void et_display_packet_sctp_initack(const sctp_initackhdr_t *const sctp)
{
  if (sctp) {
    fprintf(stdout, "\t\tSCTP.initack.init_tag               : %u\n", sctp->init_tag);
    fprintf(stdout, "\t\tSCTP.initack.a_rwnd                 : %u\n", sctp->a_rwnd);
    fprintf(stdout, "\t\tSCTP.initack.num_inbound_streams    : %u\n", sctp->num_inbound_streams);
    fprintf(stdout, "\t\tSCTP.initack.num_outbound_streams   : %u\n", sctp->num_outbound_streams);
    fprintf(stdout, "\t\tSCTP.initack.initial_tsn            : %u\n", sctp->initial_tsn);
  }
}
//------------------------------------------------------------------------------
void et_display_packet_sctp_data(const sctp_datahdr_t *const sctp)
{
  if (sctp) {
    fprintf(stdout, "\t\tSCTP.data.tsn                 : %u\n", sctp->tsn);
    fprintf(stdout, "\t\tSCTP.data.stream              : %u\n", sctp->stream);
    fprintf(stdout, "\t\tSCTP.data.ssn                 : %u\n", sctp->ssn);
    fprintf(stdout, "\t\tSCTP.data.ppid                : %u\n", sctp->ppid);

    if (sctp->ppid == 18) {
      et_display_packet_s1ap_data(&sctp->payload);
    }

    fprintf(stdout, "\t\tSCTP.data.binary_stream_allocated_size : %u\n", sctp->payload.binary_stream_allocated_size);

    if (NULL != sctp->payload.binary_stream) {
      fprintf(stdout, "\t\tSCTP.data.binary_stream       :\n");
      et_print_hex_octets(sctp->payload.binary_stream, sctp->payload.binary_stream_allocated_size);
    } else {
      fprintf(stdout, "\t\tSCTP.data.binary_stream       : NULL\n");
    }
  }
}

//------------------------------------------------------------------------------
void et_display_packet_sctp(const et_sctp_hdr_t *const sctp)
{
  if (sctp) {
    fprintf(stdout, "\t\tSCTP.src_port      : %u\n", sctp->src_port);
    fprintf(stdout, "\t\tSCTP.dst_port      : %u\n", sctp->dst_port);
    fprintf(stdout, "\t\tSCTP.chunk_type    : %s\n", et_chunk_type_cid2str(sctp->chunk_type));

    switch (sctp->chunk_type) {
      case SCTP_CID_DATA:
        et_display_packet_sctp_data(&sctp->u.data_hdr);
        break;

      case SCTP_CID_INIT:
        et_display_packet_sctp_initack(&sctp->u.init_hdr);
        break;

      case SCTP_CID_INIT_ACK:
        et_display_packet_sctp_initack(&sctp->u.init_ack_hdr);
        break;

      default:
        ;
    }
  }
}
//------------------------------------------------------------------------------
void et_display_packet_ip(const et_ip_hdr_t *const ip)
{
  if (ip) {
    fprintf(stdout, "\t\tSource address      : %s\n", et_ip2ip_str(&ip->src));
    fprintf(stdout, "\t\tDestination address : %s\n", et_ip2ip_str(&ip->dst));
  }
}

//------------------------------------------------------------------------------
void et_display_packet(const et_packet_t *const packet)
{
  if (packet) {
    fprintf(stdout, "-------------------------------------------------------------------------------\n");
    fprintf(stdout, "\tPacket:\tnum %u  | original frame number %u \n", packet->packet_number, packet->original_frame_number);
    fprintf(stdout, "\tPacket:\ttime relative to 1st packet           %ld.%06lu\n",
            packet->time_relative_to_first_packet.tv_sec, packet->time_relative_to_first_packet.tv_usec);
    fprintf(stdout, "\tPacket:\ttime relative to last tx packet       %ld.%06lu\n",
            packet->time_relative_to_last_sent_packet.tv_sec, packet->time_relative_to_last_sent_packet.tv_usec);
    fprintf(stdout, "\tPacket:\ttime relative to last_received packet %ld.%06lu\n",
            packet->time_relative_to_last_received_packet.tv_sec, packet->time_relative_to_last_received_packet.tv_usec);

    switch(packet->action) {
      case   ET_PACKET_ACTION_S1C_SEND:
        fprintf(stdout, "\tPacket:\tAction SEND\n");
        break;

      case   ET_PACKET_ACTION_S1C_RECEIVE:
        fprintf(stdout, "\tPacket:\tAction RECEIVE\n");
        break;

      default:
        fprintf(stdout, "\tPacket:\tAction UNKNOWN\n");
    }

    switch(packet->status) {
      case   ET_PACKET_STATUS_NONE:
        fprintf(stdout, "\tPacket:\tStatus NONE\n");
        break;

      case   ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT:
        fprintf(stdout, "\tPacket:\tStatus NOT_TAKEN_IN_ACCOUNT\n");
        break;

      case   ET_PACKET_STATUS_SCHEDULED_FOR_SENDING:
        fprintf(stdout, "\tPacket:\tStatus SCHEDULED_FOR_SENDING\n");
        break;

      case   ET_PACKET_STATUS_SENT:
        fprintf(stdout, "\tPacket:\tStatus SENT\n");
        break;

      case   ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING:
        fprintf(stdout, "\tPacket:\tStatus SCHEDULED_FOR_RECEIVING\n");
        break;

      case   ET_PACKET_STATUS_RECEIVED:
        fprintf(stdout, "\tPacket:\tStatus RECEIVED\n");
        break;

      default:
        fprintf(stdout, "\tPacket:\tStatus UNKNOWN\n");
    }

    et_display_packet_ip(&packet->ip_hdr);
    et_display_packet_sctp(&packet->sctp_hdr);
  }
}
//------------------------------------------------------------------------------
void et_display_scenario(const et_scenario_t *const scenario)
{
  et_packet_t *packet = NULL;

  if (scenario) {
    fprintf(stdout, "Scenario: %s\n", (scenario->name != NULL) ? (char *)scenario->name:"UNKNOWN NAME");
    packet = scenario->list_packet;

    while (packet) {
      et_display_packet(packet);
      packet = packet->next;
    }
  }
}
