/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2007 INESC Porto
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Gustavo J. A. M. Carneiro  <gjc@inescporto.pt>
 */

#include <cmath>

#include "ns3/assert.h"
#include "ns3/log.h"

#include "olsr-header.h"

#define IPV4_ADDRESS_SIZE 4
#define OLSR_MSG_HEADER_SIZE 12
#define OLSR_PKT_HEADER_SIZE 4

namespace ns3 {
namespace olsr {


NS_LOG_COMPONENT_DEFINE ("OlsrHeader");

/// Scaling factor used in RFC 3626.
#define OLSR_C 0.0625

///
/// \brief Converts a decimal number of seconds to the mantissa/exponent format.
///
/// \param seconds decimal number of seconds we want to convert.
/// \return the number of seconds in mantissa/exponent format.
///
uint8_t
SecondsToEmf (double seconds)
{
  int a, b = 0;

  // find the largest integer 'b' such that: T/C >= 2^b
  for (b = 0; (seconds/OLSR_C) >= (1 << b); ++b)
    ;
  NS_ASSERT ((seconds/OLSR_C) < (1 << b));
  b--;
  NS_ASSERT ((seconds/OLSR_C) >= (1 << b));

  // compute the expression 16*(T/(C*(2^b))-1), which may not be a integer
  double tmp = 16*(seconds/(OLSR_C*(1<<b))-1);

  // round it up.  This results in the value for 'a'
  a = (int) std::ceil (tmp);

  // if 'a' is equal to 16: increment 'b' by one, and set 'a' to 0
  if (a == 16)
    {
      b += 1;
      a = 0;
    }

  // now, 'a' and 'b' should be integers between 0 and 15,
  NS_ASSERT (a >= 0 && a < 16);
  NS_ASSERT (b >= 0 && b < 16);

  // the field will be a byte holding the value a*16+b
  return (uint8_t)((a << 4) | b);
}

///
/// \brief Converts a number of seconds in the mantissa/exponent format to a decimal number.
///
/// \param olsr_format number of seconds in mantissa/exponent format.
/// \return the decimal number of seconds.
///
double
EmfToSeconds (uint8_t olsrFormat)
{
  int a = (olsrFormat >> 4);
  int b = (olsrFormat & 0xf);
  // value = C*(1+a/16)*2^b [in seconds]
  return OLSR_C * (1 + a/16.0) * (1 << b);
}



// ---------------- OLSR Packet -------------------------------

NS_OBJECT_ENSURE_REGISTERED (PacketHeader);

PacketHeader::PacketHeader ()
{
}

PacketHeader::~PacketHeader ()
{
}

TypeId
PacketHeader::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::olsr::PacketHeader")
    .SetParent<Header> ()
    .AddConstructor<PacketHeader> ()
  ;
  return tid;
}
TypeId
PacketHeader::GetInstanceTypeId (void) const
{
  return GetTypeId ();
}

uint32_t 
PacketHeader::GetSerializedSize (void) const
{
  return OLSR_PKT_HEADER_SIZE;
}

void 
PacketHeader::Print (std::ostream &os) const
{
  // TODO
}

void
PacketHeader::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;
  i.WriteHtonU16 (m_packetLength);
  i.WriteHtonU16 (m_packetSequenceNumber);
}

uint32_t
PacketHeader::Deserialize (Buffer::Iterator start)
{
  Buffer::Iterator i = start;
  m_packetLength  = i.ReadNtohU16 ();
  m_packetSequenceNumber = i.ReadNtohU16 ();
  return GetSerializedSize ();
}


// ---------------- OLSR Message -------------------------------

NS_OBJECT_ENSURE_REGISTERED (MessageHeader);

MessageHeader::MessageHeader ()
  : m_messageType (MessageHeader::MessageType (0))
{
}

MessageHeader::~MessageHeader ()
{
}

TypeId
MessageHeader::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::olsr::MessageHeader")
    .SetParent<Header> ()
    .AddConstructor<MessageHeader> ()
  ;
  return tid;
}
TypeId
MessageHeader::GetInstanceTypeId (void) const
{
  return GetTypeId ();
}

uint32_t
MessageHeader::GetSerializedSize (void) const
{
  uint32_t size = OLSR_MSG_HEADER_SIZE;
  switch (m_messageType)
    {
    case MID_MESSAGE:
      size += m_message.mid.GetSerializedSize ();
      break;
    case HELLO_MESSAGE:
      NS_LOG_DEBUG ("Hello Message Size: " << size << " + " << m_message.hello.GetSerializedSize ());
      size += m_message.hello.GetSerializedSize ();
      break;
    case TC_MESSAGE:
      size += m_message.tc.GetSerializedSize ();
      break;
    case HNA_MESSAGE:
      size += m_message.hna.GetSerializedSize ();
      break;
    default:
      NS_ASSERT (false);
    }
  return size;
}

void 
MessageHeader::Print (std::ostream &os) const
{
  // TODO
}

void
MessageHeader::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;
  i.WriteU8 (m_messageType);
  i.WriteU8 (m_vTime);
  i.WriteHtonU16 (GetSerializedSize ());
  i.WriteHtonU32 (m_originatorAddress.Get ());
  i.WriteU8 (m_timeToLive);
  i.WriteU8 (m_hopCount);
  i.WriteHtonU16 (m_messageSequenceNumber);

  switch (m_messageType)
    {
    case MID_MESSAGE:
      m_message.mid.Serialize (i);
      break;
    case HELLO_MESSAGE:
      m_message.hello.Serialize (i);
      break;
    case TC_MESSAGE:
      m_message.tc.Serialize (i);
      break;
    case HNA_MESSAGE:
      m_message.hna.Serialize (i);
      break;
    default:
      NS_ASSERT (false);
    }

}

uint32_t
MessageHeader::Deserialize (Buffer::Iterator start)
{
  uint32_t size;
  Buffer::Iterator i = start;
  m_messageType  = (MessageType) i.ReadU8 ();
  NS_ASSERT (m_messageType >= HELLO_MESSAGE && m_messageType <= HNA_MESSAGE);
  m_vTime  = i.ReadU8 ();
  m_messageSize  = i.ReadNtohU16 ();
  m_originatorAddress = Ipv4Address (i.ReadNtohU32 ());
  m_timeToLive  = i.ReadU8 ();
  m_hopCount  = i.ReadU8 ();
  m_messageSequenceNumber = i.ReadNtohU16 ();
  size = OLSR_MSG_HEADER_SIZE;
  switch (m_messageType)
    {
    case MID_MESSAGE:
      size += m_message.mid.Deserialize (i, m_messageSize - OLSR_MSG_HEADER_SIZE);
      break;
    case HELLO_MESSAGE:
      size += m_message.hello.Deserialize (i, m_messageSize - OLSR_MSG_HEADER_SIZE);
      break;
    case TC_MESSAGE:
      size += m_message.tc.Deserialize (i, m_messageSize - OLSR_MSG_HEADER_SIZE);
      break;
    case HNA_MESSAGE:
      size += m_message.hna.Deserialize (i, m_messageSize - OLSR_MSG_HEADER_SIZE);
      break;
    default:
      NS_ASSERT (false);
    }
  return size;
}


// ---------------- OLSR MID Message -------------------------------

uint32_t 
MessageHeader::Mid::GetSerializedSize (void) const
{
  return this->interfaceAddresses.size () * IPV4_ADDRESS_SIZE;
}

void 
MessageHeader::Mid::Print (std::ostream &os) const
{
  // TODO
}

void
MessageHeader::Mid::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;

  for (std::vector<Ipv4Address>::const_iterator iter = this->interfaceAddresses.begin ();
       iter != this->interfaceAddresses.end (); iter++)
    {
      i.WriteHtonU32 (iter->Get ());
    }
}

uint32_t
MessageHeader::Mid::Deserialize (Buffer::Iterator start, uint32_t messageSize)
{
  Buffer::Iterator i = start;

  this->interfaceAddresses.clear ();
  NS_ASSERT (messageSize % IPV4_ADDRESS_SIZE == 0);

  int numAddresses = messageSize / IPV4_ADDRESS_SIZE;
  this->interfaceAddresses.erase (this->interfaceAddresses.begin (),
                                  this->interfaceAddresses.end ());
  for (int n = 0; n < numAddresses; ++n)
    this->interfaceAddresses.push_back (Ipv4Address (i.ReadNtohU32 ()));
  return GetSerializedSize ();
}



// ---------------- OLSR HELLO Message -------------------------------

uint32_t 
MessageHeader::Hello::GetSerializedSize (void) const
{
  uint32_t size = 4;
  for (std::vector<LinkMessage>::const_iterator iter = this->linkMessages.begin ();
       iter != this->linkMessages.end (); iter++)
    {
      const LinkMessage &lm = *iter;
      size += 4;
      size += IPV4_ADDRESS_SIZE * lm.neighborInterfaceAddresses.size ();
    }
  return size;
}

void 
MessageHeader::Hello::Print (std::ostream &os) const
{
  // TODO
}

void
MessageHeader::Hello::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;

  i.WriteU16 (0); // Reserved
  i.WriteU8 (this->hTime);
  i.WriteU8 (this->willingness);

  for (std::vector<LinkMessage>::const_iterator iter = this->linkMessages.begin ();
       iter != this->linkMessages.end (); iter++)
    {
      const LinkMessage &lm = *iter;

      i.WriteU8 (lm.linkCode);
      i.WriteU8 (0); // Reserved

      // The size of the link message, counted in bytes and measured
      // from the beginning of the "Link Code" field and until the
      // next "Link Code" field (or - if there are no more link types
      // - the end of the message).
      i.WriteHtonU16 (4 + lm.neighborInterfaceAddresses.size () * IPV4_ADDRESS_SIZE);

      for (std::vector<Ipv4Address>::const_iterator neigh_iter = lm.neighborInterfaceAddresses.begin ();
           neigh_iter != lm.neighborInterfaceAddresses.end (); neigh_iter++)
        {
          i.WriteHtonU32 (neigh_iter->Get ());
        }
    }
}

uint32_t
MessageHeader::Hello::Deserialize (Buffer::Iterator start, uint32_t messageSize)
{
  Buffer::Iterator i = start;

  NS_ASSERT (messageSize >= 4);

  this->linkMessages.clear ();

  uint16_t helloSizeLeft = messageSize;

  i.ReadNtohU16 (); // Reserved
  this->hTime = i.ReadU8 ();
  this->willingness = i.ReadU8 ();

  helloSizeLeft -= 4;

  while (helloSizeLeft)
    {
      LinkMessage lm;
      NS_ASSERT (helloSizeLeft >= 4);
      lm.linkCode = i.ReadU8 ();
      i.ReadU8 (); // Reserved
      uint16_t lmSize = i.ReadNtohU16 ();
      NS_ASSERT ((lmSize - 4) % IPV4_ADDRESS_SIZE == 0);
      for (int n = (lmSize - 4) / IPV4_ADDRESS_SIZE; n; --n)
        {
          lm.neighborInterfaceAddresses.push_back (Ipv4Address (i.ReadNtohU32 ()));
        }
      helloSizeLeft -= lmSize;
      this->linkMessages.push_back (lm);
    }

  return messageSize;
}



// ---------------- OLSR TC Message -------------------------------

uint32_t 
MessageHeader::Tc::GetSerializedSize (void) const
{
  return 4 + this->neighborAddresses.size () * IPV4_ADDRESS_SIZE;
}

void 
MessageHeader::Tc::Print (std::ostream &os) const
{
  // TODO
}

void
MessageHeader::Tc::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;

  i.WriteHtonU16 (this->ansn);
  i.WriteHtonU16 (0); // Reserved

  for (std::vector<Ipv4Address>::const_iterator iter = this->neighborAddresses.begin ();
       iter != this->neighborAddresses.end (); iter++)
    {
      i.WriteHtonU32 (iter->Get ());
    }
}

uint32_t
MessageHeader::Tc::Deserialize (Buffer::Iterator start, uint32_t messageSize)
{
  Buffer::Iterator i = start;

  this->neighborAddresses.clear ();
  NS_ASSERT (messageSize >= 4);

  this->ansn = i.ReadNtohU16 ();
  i.ReadNtohU16 (); // Reserved

  NS_ASSERT ((messageSize - 4) % IPV4_ADDRESS_SIZE == 0);
  int numAddresses = (messageSize - 4) / IPV4_ADDRESS_SIZE;
  this->neighborAddresses.clear ();
  for (int n = 0; n < numAddresses; ++n)
    this->neighborAddresses.push_back (Ipv4Address (i.ReadNtohU32 ()));

  return messageSize;
}


// ---------------- OLSR HNA Message -------------------------------

uint32_t 
MessageHeader::Hna::GetSerializedSize (void) const
{
  return 2*this->associations.size () * IPV4_ADDRESS_SIZE;
}

void 
MessageHeader::Hna::Print (std::ostream &os) const
{
  // TODO
}

void
MessageHeader::Hna::Serialize (Buffer::Iterator start) const
{
  Buffer::Iterator i = start;

  for (size_t n = 0; n < this->associations.size (); ++n)
    {
      i.WriteHtonU32 (this->associations[n].address.Get ());
      i.WriteHtonU32 (this->associations[n].mask.Get ());
    }
}

uint32_t
MessageHeader::Hna::Deserialize (Buffer::Iterator start, uint32_t messageSize)
{
  Buffer::Iterator i = start;

  NS_ASSERT (messageSize % (IPV4_ADDRESS_SIZE*2) == 0);
  int numAddresses = messageSize / IPV4_ADDRESS_SIZE / 2;
  this->associations.clear ();
  for (int n = 0; n < numAddresses; ++n)
    {
      Ipv4Address address (i.ReadNtohU32 ());
      Ipv4Mask mask (i.ReadNtohU32 ());
      this->associations.push_back ((Association) { address, mask});
    }
  return messageSize;
}

}
}  // namespace olsr, ns3

