text: /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
  /*
   * Copyright (c) 2008 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 Carneiro  <gjc@inescporto.pt>
   */
  
  #include <cstdlib>
  #include "pyviz.h"
  #include "ns3/simulator.h"
  #include "ns3/config.h"
  #include "ns3/node-list.h"
  #include "ns3/wifi-net-device.h"
  #include "ns3/ppp-header.h"
  #include "ns3/wifi-mac-header.h"
  #include "ns3/ethernet-header.h"
  #include "ns3/log.h"
  #include "ns3/abort.h"
  
  #include "visual-simulator-impl.h"
  
  #include <sstream>
  
  NS_LOG_COMPONENT_DEFINE ("PyViz");
  
  #define NUM_LAST_PACKETS 10
  
  static
  std::vector<std::string>
  PathSplit (std::string str)
  {
    std::vector<std::string> results;
    size_t cutAt;
    while ((cutAt = str.find_first_of ('/')) != str.npos)
      {
        if(cutAt > 0)
          {
            results.push_back (str.substr (0,cutAt));
          }
        str = str.substr (cutAt+1);
      }
    if (str.length () > 0)
      {
        results.push_back (str);
      }
    return results;
  }
  
  
  namespace ns3 {
  
  static PyViz* g_visualizer = NULL;
  
  
  
  struct PyVizPacketTag : public Tag
  {
    static TypeId GetTypeId (void);
    virtual TypeId GetInstanceTypeId (void) const;
    virtual uint32_t GetSerializedSize (void) const;
    virtual void Serialize (TagBuffer buf) const;
    virtual void Deserialize (TagBuffer buf);
    virtual void Print (std::ostream &os) const;
    PyVizPacketTag ();
  
    uint32_t m_packetId;
  };
  
  
  TypeId 
  PyVizPacketTag::GetTypeId (void)
  {
    static TypeId tid = TypeId ("ns3::PyVizPacketTag")
      .SetParent<Tag> ()
      .SetGroupName ("Visualizer")
      .AddConstructor<PyVizPacketTag> ()
    ;
    return tid;
  }
  TypeId 
  PyVizPacketTag::GetInstanceTypeId (void) const
  {
    return GetTypeId ();
  }
  uint32_t 
  PyVizPacketTag::GetSerializedSize (void) const
  {
    return 4;
  }
  void 
  PyVizPacketTag::Serialize (TagBuffer buf) const
  {
    buf.WriteU32 (m_packetId);
  }
  void 
  PyVizPacketTag::Deserialize (TagBuffer buf)
  {
    m_packetId = buf.ReadU32 ();
  }
  void 
  PyVizPacketTag::Print (std::ostream &os) const
  {
    os << "PacketId=" << m_packetId;
  }
  PyVizPacketTag::PyVizPacketTag ()
    : Tag () 
  {
  }
  
  
  
  PyViz::PyViz ()
  {
    NS_LOG_FUNCTION_NOARGS ();
    NS_ASSERT (g_visualizer == NULL);
    g_visualizer = this;
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx",
                     MakeCallback (&PyViz::TraceNetDevTxWifi, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx",
                     MakeCallback (&PyViz::TraceNetDevRxWifi, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacTx",
                     MakeCallback (&PyViz::TraceNetDevTxCsma, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacRx",
                     MakeCallback (&PyViz::TraceNetDevRxCsma, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacPromiscRx",
                     MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/TxQueue/Drop",
                     MakeCallback (&PyViz::TraceDevQueueDrop, this));
  
    Config::Connect ("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
                     MakeCallback (&PyViz::TraceIpv4Drop, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx",
                     MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacRx",
                     MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
  
    // WiMax
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Tx",
                     MakeCallback (&PyViz::TraceNetDevTxWimax, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Rx",
                     MakeCallback (&PyViz::TraceNetDevRxWimax, this));
  
    // LTE
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Tx",
                     MakeCallback (&PyViz::TraceNetDevTxLte, this));
  
    Config::Connect ("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Rx",
                     MakeCallback (&PyViz::TraceNetDevRxLte, this));
  }
  
  void
  PyViz::RegisterCsmaLikeDevice (std::string const &deviceTypeName)
  {
    TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
  
    std::ostringstream sstream;
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/MacTx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxCsma, this));
  
    sstream.str ("");
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxCsma, this));
  
    sstream.str ("");
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/PromiscRx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
  }
  
  void
  PyViz::RegisterWifiLikeDevice (std::string const &deviceTypeName)
  {
    TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
  
    std::ostringstream sstream;
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Tx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxWifi, this));
  
    sstream.str ("");
    sstream <<"/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxWifi, this));
  }
  
  void
  PyViz::RegisterPointToPointLikeDevice (std::string const &deviceTypeName)
  {
    TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
  
    std::ostringstream sstream;
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
  
    sstream.str ("");
    sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
    Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
  }
  
  void
  PyViz::SetPacketCaptureOptions (uint32_t nodeId, PacketCaptureOptions options)
  {
    NS_LOG_DEBUG ("  SetPacketCaptureOptions " << nodeId
                                               << " PacketCaptureOptions (headers size = " << options.headers.size ()
                                               << " mode = " << options.mode << " numLastPackets = " << options.numLastPackets
                                               << ")");
    m_packetCaptureOptions[nodeId] = options;
  }
  
  void
  PyViz::RegisterDropTracePath (std::string const &tracePath)
  {
    Config::Connect (tracePath, MakeCallback (&PyViz::TraceDevQueueDrop, this));
  }
  
  PyViz::~PyViz ()
  {
    NS_LOG_FUNCTION_NOARGS ();
  
    NS_ASSERT (g_visualizer == this);
    g_visualizer = NULL;
  }
  
  void PyViz::DoPause (std::string const &message)
  {
    m_pauseMessages.push_back (message);
    m_stop = true;
    NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": Have "
                                                  << g_visualizer->m_pauseMessages.size () << " pause messages");
  }
  
  void PyViz::Pause (std::string const &message)
  {
    NS_ASSERT (g_visualizer);
    g_visualizer->DoPause (message);
  }
  
  std::vector<std::string>
  PyViz::GetPauseMessages () const
  {
    NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": GetPauseMessages: have "
                                                  << g_visualizer->m_pauseMessages.size () << " pause messages");
    return m_pauseMessages;
  }
  
  
  void
  PyViz::CallbackStopSimulation ()
  {
    NS_LOG_FUNCTION_NOARGS ();
    if (m_runUntil <= Simulator::Now ())
      {
        Simulator::Stop (Seconds (0)); // Stop right now
        m_stop = true;
      }
  }
  
  void
  PyViz::SimulatorRunUntil (Time time)
  {
    NS_LOG_LOGIC ("SimulatorRunUntil " << time << " (now is " << Simulator::Now () << ")");
  
    m_pauseMessages.clear ();
    m_transmissionSamples.clear ();
    m_packetDrops.clear ();
  
    Time expirationTime = Simulator::Now () - Seconds (10);
  
    // Clear very old transmission records
    for (std::map<TxRecordKey, TxRecordValue>::iterator iter = m_txRecords.begin ();
         iter != m_txRecords.end ();)
      {
        if (iter->second.time < expirationTime)
          {
            m_txRecords.erase (iter++);
          }
        else
          {
            iter++;
          }
      }
  
    // Clear very old packets of interest
    for (std::map<uint32_t, Time>::iterator iter = m_packetsOfInterest.begin ();
         iter != m_packetsOfInterest.end ();)
      {
        if (iter->second < expirationTime)
          {
            m_packetsOfInterest.erase (iter++);
          }
        else
          {
            iter++;
          }
      }
  
    if (Simulator::Now () >= time)
      {
        return;
      }
    // Schedule a dummy callback function for the target time, to make
    // sure we stop at the right time.  Otherwise, simulations with few
    // events just appear to "jump" big chunks of time.
    NS_LOG_LOGIC ("Schedule dummy callback to be called in " << (time - Simulator::Now ()));
    m_runUntil = time;
    m_stop = false;
    Simulator::ScheduleWithContext (Simulator::NO_CONTEXT, time - Simulator::Now (), &PyViz::CallbackStopSimulation, this);
  
    Ptr<SimulatorImpl> impl = Simulator::GetImplementation ();
    Ptr<VisualSimulatorImpl> visualImpl = DynamicCast<VisualSimulatorImpl> (impl);
    if (visualImpl)
      {
        visualImpl->RunRealSimulator ();
      }
    else
      {
        impl->Run ();
      }
  }
  
  bool PyViz::TransmissionSampleKey::operator < (PyViz::TransmissionSampleKey const &other) const
  {
    if (this->transmitter < other.transmitter)
      {
        return true;
      }
    if (this->transmitter != other.transmitter)
      {
        return false;
      }
    if (this->receiver < other.receiver)
      {
        return true;
      }
    if (this->receiver != other.receiver)
      {
        return false;
      }
    if (this->channel < other.channel)
      {
        return true;
      }
    else
      {
        return false;
      }
  }
  
  bool PyViz::TransmissionSampleKey::operator == (PyViz::TransmissionSampleKey const &other) const
  {
    bool retval = (transmitter == other.transmitter) &&
      (receiver == other.receiver) &&
      (channel == other.channel);
    return retval;
  }
  
  
  PyViz::NetDeviceStatistics &
  PyViz::FindNetDeviceStatistics (int node, int interface)
  {
    std::map<uint32_t, std::vector<NetDeviceStatistics> >::iterator nodeStatsIter = m_nodesStatistics.find (node);
    std::vector<NetDeviceStatistics> *stats;
    if (nodeStatsIter == m_nodesStatistics.end ())
      {
        stats = &m_nodesStatistics[node];
        stats->resize (NodeList::GetNode (node)->GetNDevices ());
      }
    else
      {
        stats = &(nodeStatsIter->second);
      }
    NetDeviceStatistics &devStats = (*stats)[interface];
    return devStats;
  }
  
  bool PyViz::GetPacketCaptureOptions (uint32_t nodeId, const PacketCaptureOptions **outOptions) const
  {
    std::map<uint32_t, PacketCaptureOptions>::const_iterator iter = m_packetCaptureOptions.find (nodeId);
    if (iter == m_packetCaptureOptions.end ())
      {
        return false;
      }
    else
      {
        *outOptions = &iter->second;
        return true;
      }
  }
  
  bool PyViz::FilterPacket (Ptr<const Packet> packet, const PacketCaptureOptions &options)
  {
    switch (options.mode)
      {
      case PACKET_CAPTURE_DISABLED:
        return false;
  
      case PACKET_CAPTURE_FILTER_HEADERS_OR:
        {
          PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
          while (metadataIterator.HasNext ())
            {
              PacketMetadata::Item item = metadataIterator.Next ();
              if (options.headers.find (item.tid) != options.headers.end ())
                {
                  return true;
                }
            }
          return false;
        }
  
      case PACKET_CAPTURE_FILTER_HEADERS_AND:
        {
          std::set<TypeId> missingHeaders (options.headers);
          PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
          while (metadataIterator.HasNext ())
            {
              PacketMetadata::Item item = metadataIterator.Next ();
              std::set<TypeId>::iterator missingIter = missingHeaders.find (item.tid);
              if (missingIter != missingHeaders.end ())
                {
                  missingHeaders.erase (missingIter);
                }
            }
          if (missingHeaders.size () == 0)
            {
              return true;
            }
          else
            {
              return false;
            }
        }
  
      default:
        NS_FATAL_ERROR ("should not be reached");
        return false;
      }
  }
  
  void
  PyViz::TraceDevQueueDrop (std::string context, Ptr<const Packet> packet)
  {
    NS_LOG_FUNCTION (context << packet->GetUid ());
    std::vector<std::string> splitPath = PathSplit (context);
    int nodeIndex = std::atoi (splitPath[1].c_str ());
    Ptr<Node> node = NodeList::GetNode (nodeIndex);
  
    if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
      {
        // if the transmitting node is not "of interest", we still
        // record the transmission if it is a packet of interest.
        if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
          {
            NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
            return;
          }
      }
  
    // ---- "last packets"
    const PacketCaptureOptions *captureOptions;
    if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
      {
        LastPacketsSample &last = m_lastPackets[nodeIndex];
        PacketSample lastPacket;
        lastPacket.time = Simulator::Now ();
        lastPacket.packet = packet->Copy ();
        lastPacket.device = NULL;
        last.lastDroppedPackets.push_back (lastPacket);
        while (last.lastDroppedPackets.size () > captureOptions->numLastPackets)
          {
            last.lastDroppedPackets.erase (last.lastDroppedPackets.begin ());
          }
      }
  
    std::map<Ptr<Node>, uint32_t>::iterator iter = m_packetDrops.find (node);
    if (iter == m_packetDrops.end ())
      {
        m_packetDrops[node] = packet->GetSize ();
      }
    else
      {
        iter->second += packet->GetSize ();
      }
  }
  
  void
  PyViz::TraceIpv4Drop (std::string context, ns3::Ipv4Header const &hdr, Ptr<const Packet> packet,
                        ns3::Ipv4L3Protocol::DropReason reason, Ptr<Ipv4> dummy_ipv4, uint32_t interface)
  {
    Ptr<Packet> packetCopy = packet->Copy ();
    packetCopy->AddHeader (hdr);
    TraceDevQueueDrop (context, packetCopy);
  }
  
  
  // --------- TX device tracing -------------------
  
  void
  PyViz::TraceNetDevTxCommon (std::string const &context, Ptr<const Packet> packet,
                              Mac48Address const &destinationAddress)
  {
    NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
  
    std::vector<std::string> splitPath = PathSplit (context);
    int nodeIndex = std::atoi (splitPath[1].c_str ());
    int devIndex = std::atoi (splitPath[3].c_str ());
    Ptr<Node> node = NodeList::GetNode (nodeIndex);
    Ptr<NetDevice> device = node->GetDevice (devIndex);
  
    // ---- statistics
    NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
    ++stats.transmittedPackets;
    stats.transmittedBytes += packet->GetSize ();
  
    // ---- "last packets"
    const PacketCaptureOptions *captureOptions;
    if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
      {
        LastPacketsSample &last = m_lastPackets[nodeIndex];
        TxPacketSample lastPacket;
        lastPacket.time = Simulator::Now ();
        lastPacket.packet = packet->Copy ();
        lastPacket.device = device;
        lastPacket.to = destinationAddress;
        last.lastTransmittedPackets.push_back (lastPacket);
        while (last.lastTransmittedPackets.size () > captureOptions->numLastPackets)
          {
            last.lastTransmittedPackets.erase (last.lastTransmittedPackets.begin ());
          }
      }
  
    // ---- transmissions records
  
    if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
      {
        // if the transmitting node is not "of interest", we still
        // record the transmission if it is a packet of interest.
        if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
          {
            NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
            return;
          }
      }
    else
      {
        // We will follow this packet throughout the network.
        m_packetsOfInterest[packet->GetUid ()] = Simulator::Now ();
      }
  
    TxRecordValue record = { Simulator::Now (), node, false };
    if (destinationAddress == device->GetBroadcast ())
      {
        record.isBroadcast = true;
      }
  
    m_txRecords[TxRecordKey (device->GetChannel (), packet->GetUid ())] = record;
  
    PyVizPacketTag tag;
    //packet->RemovePacketTag (tag);
    tag.m_packetId = packet->GetUid ();
    packet->AddByteTag (tag);
  }
  
  void
  PyViz::TraceNetDevTxWifi (std::string context, Ptr<const Packet> packet)
  {
    NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
  
    /*
     *  To DS    From DS   Address 1    Address 2    Address 3    Address 4 
     *----------------------------------------------------------------------
     *    0        0       Destination  Source        BSSID         N/A
     *    0        1       Destination  BSSID         Source        N/A
     *    1        0       BSSID        Source        Destination   N/A
     *    1        1       Receiver     Transmitter   Destination   Source
     */
    WifiMacHeader hdr;
    NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
    Mac48Address destinationAddress;
    if (hdr.IsToDs () && !hdr.IsFromDs ())
      {
        destinationAddress = hdr.GetAddr3 ();
      }
    else if (!hdr.IsToDs () && hdr.IsFromDs ())
      {
        destinationAddress = hdr.GetAddr1 ();
      }
    else if (!hdr.IsToDs () && !hdr.IsFromDs ())
      {
        destinationAddress = hdr.GetAddr1 ();
      }
    else
      {
        destinationAddress = hdr.GetAddr3 ();
      }
    TraceNetDevTxCommon (context, packet, destinationAddress);
  }
  
  
  void
  PyViz::TraceNetDevTxCsma (std::string context, Ptr<const Packet> packet)
  {
    EthernetHeader ethernetHeader;
    NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
    TraceNetDevTxCommon (context, packet, ethernetHeader.GetDestination ());
  }
  
  void
  PyViz::TraceNetDevTxPointToPoint (std::string context, Ptr<const Packet> packet)
  {
    TraceNetDevTxCommon (context, packet, Mac48Address ());
  }
  
  
  
  
  // --------- RX device tracing -------------------
  
  void
  PyViz::TraceNetDevRxCommon (std::string const &context, Ptr<const Packet> packet, Mac48Address const &from)
  {
    uint32_t uid;
    PyVizPacketTag tag;
    if (packet->FindFirstMatchingByteTag (tag))
      {
        uid = tag.m_packetId;
      }
    else
      {
        //NS_ASSERT (0);
        NS_LOG_WARN ("Packet has no byte tag; wimax link?");
        uid = packet->GetUid ();
      }
  
    NS_LOG_FUNCTION (context << uid);
    std::vector<std::string> splitPath = PathSplit (context);
    int nodeIndex = std::atoi (splitPath[1].c_str ());
    int devIndex = std::atoi (splitPath[3].c_str ());
  
    // ---- statistics
    NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
    ++stats.receivedPackets;
    stats.receivedBytes += packet->GetSize ();
  
    Ptr<Node> node = NodeList::GetNode (nodeIndex);
    Ptr<NetDevice> device = node->GetDevice (devIndex);
  
    // ---- "last packets"
    const PacketCaptureOptions *captureOptions;
    if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
      {
        LastPacketsSample &last = m_lastPackets[nodeIndex];
        RxPacketSample lastPacket;
        lastPacket.time = Simulator::Now ();
        lastPacket.packet = packet->Copy ();
        lastPacket.device = device;
        lastPacket.from = from;
        last.lastReceivedPackets.push_back (lastPacket);
        while (last.lastReceivedPackets.size () > captureOptions->numLastPackets)
          {
            last.lastReceivedPackets.erase (last.lastReceivedPackets.begin ());
          }
      }
  
    // ---- transmissions
    if (m_packetsOfInterest.find (uid) == m_packetsOfInterest.end ())
      {
        NS_LOG_DEBUG ("RX Packet " << uid << " is not of interest");
        return;
      }
  
    Ptr<Channel> channel = device->GetChannel ();
  
    std::map<TxRecordKey, TxRecordValue>::iterator recordIter = 
      m_txRecords.find (TxRecordKey (channel, uid));
  
    if (recordIter == m_txRecords.end ())
      {
        NS_LOG_DEBUG ("RX Packet " << uid << " was not transmitted?!");
        return;
      }
  
    TxRecordValue &record = recordIter->second;
  
    if (record.srcNode == node)
      {
        NS_LOG_WARN ("Node " << node->GetId () << " receiving back the same packet (UID=" << uid
                             << ") it had previously transmitted, on the same channel!");
        return;
      }
  
    TransmissionSampleKey key = { record.srcNode, node, channel };
  
  #ifdef  NS3_LOG_ENABLE
    NS_LOG_DEBUG ("m_transmissionSamples begin:");
    if (g_log.IsEnabled (ns3::LOG_DEBUG))
      {
        for (std::map<TransmissionSampleKey,TransmissionSampleValue>::const_iterator iter
               = m_transmissionSamples.begin (); iter != m_transmissionSamples.end (); iter++)
          {
            NS_LOG_DEBUG (iter->first.transmitter<<"/"<<iter->first.transmitter->GetId () << ", "
                                                 << iter->first.receiver<<"/"<<iter->first.receiver->GetId ()
                                                 << ", " << iter->first.channel << " => " << iter->second.bytes << " (@ " << &iter->second << ")");
          }
      }
    NS_LOG_DEBUG ("m_transmissionSamples end.");
  #endif
  
    std::map<TransmissionSampleKey,TransmissionSampleValue>::iterator
      iter = m_transmissionSamples.find (key);
  
    if (iter == m_transmissionSamples.end ())
      {
        TransmissionSampleValue sample = { packet->GetSize () };
        NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId () << " to "
                                  << key.receiver<<"/"<<key.receiver->GetId ()
                                  << " channel " << channel << ": " << packet->GetSize ()
                                  << " bytes more. => new sample with " << packet->GetSize () << " bytes.");
        m_transmissionSamples[key] = sample;
      }
    else
      {
        TransmissionSampleValue &sample = iter->second;
        NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId () << " to "
                                  << key.receiver<<"/"<<key.receiver->GetId ()
                                  << " channel " << channel << ": " << packet->GetSize ()
                                  << " bytes more. => sample " << &sample << " with bytes " << sample.bytes);
  
        sample.bytes += packet->GetSize ();
      }
  }
  
  void
  PyViz::TraceNetDevRxWifi (std::string context, Ptr<const Packet> packet)
  {
    NS_LOG_FUNCTION (context << packet->GetUid ());
  
  
    /*
     *  To DS    From DS   Address 1    Address 2    Address 3    Address 4 
     *----------------------------------------------------------------------
     *    0        0       Destination  Source        BSSID         N/A
     *    0        1       Destination  BSSID         Source        N/A
     *    1        0       BSSID        Source        Destination   N/A
     *    1        1       Receiver     Transmitter   Destination   Source
     */
    WifiMacHeader hdr;
    NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
    Mac48Address sourceAddress;
    if (hdr.IsToDs () && !hdr.IsFromDs ())
      {
        sourceAddress = hdr.GetAddr2 ();
      }
    else if (!hdr.IsToDs () && hdr.IsFromDs ())
      {
        sourceAddress = hdr.GetAddr3 ();
      }
    else if (!hdr.IsToDs () && !hdr.IsFromDs ())
      {
        sourceAddress = hdr.GetAddr2 ();
      }
    else
      {
        sourceAddress = hdr.GetAddr4 ();
      }
  
    TraceNetDevRxCommon (context, packet, sourceAddress);
  }
  
  
  
  void
  PyViz::TraceNetDevRxCsma (std::string context, Ptr<const Packet> packet)
  {
    EthernetHeader ethernetHeader;
    NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
    TraceNetDevRxCommon (context, packet, ethernetHeader.GetSource ());
  }
  
  void
  PyViz::TraceNetDevRxPointToPoint (std::string context, Ptr<const Packet> packet)
  {
    TraceNetDevRxCommon (context, packet, Mac48Address ());
  }
  
  void
  PyViz::TraceNetDevPromiscRxCsma (std::string context, Ptr<const Packet> packet)
  {
    EthernetHeader ethernetHeader;
    NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
  
    NetDevice::PacketType packetType = NetDevice::PACKET_OTHERHOST; // FIXME
  
    // Other packet types are already being received by
    // TraceNetDevRxCsma; we don't want to receive them twice.
    if (packetType == NetDevice::PACKET_OTHERHOST)
      {
        TraceNetDevRxCommon (context, packet, ethernetHeader.GetDestination ());
      }
  }
  
  void
  PyViz::TraceNetDevTxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &destination)
  {
    NS_LOG_FUNCTION (context);
    TraceNetDevTxCommon (context, packet, destination);
  }
  
  void
  PyViz::TraceNetDevRxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &source)
  {
    NS_LOG_FUNCTION (context);
    TraceNetDevRxCommon (context, packet, source);
  }
  
  void
  PyViz::TraceNetDevTxLte (std::string context, Ptr<const Packet> packet, Mac48Address const &destination)
  {
    NS_LOG_FUNCTION (context);
    TraceNetDevTxCommon (context, packet, destination);
  }
  
  void
  PyViz::TraceNetDevRxLte (std::string context, Ptr<const Packet> packet, Mac48Address const &source)
  {
    NS_LOG_FUNCTION (context);
    TraceNetDevRxCommon (context, packet, source);
  }
  
  // ---------------------
  
  PyViz::TransmissionSampleList
  PyViz::GetTransmissionSamples () const
  {
    NS_LOG_DEBUG ("GetTransmissionSamples BEGIN");
    TransmissionSampleList list;
    for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator
         iter = m_transmissionSamples.begin ();
         iter !=  m_transmissionSamples.end ();
         iter++)
      {
        TransmissionSample sample;
        sample.transmitter = iter->first.transmitter;
        sample.receiver = iter->first.receiver;
        sample.channel = iter->first.channel;
        sample.bytes = iter->second.bytes;
        NS_LOG_DEBUG ("from " << sample.transmitter->GetId () << " to " << sample.receiver->GetId ()
                              << ": " << sample.bytes << " bytes.");
        list.push_back (sample);
      }
    NS_LOG_DEBUG ("GetTransmissionSamples END");
    return list;
  }
  
  PyViz::PacketDropSampleList
  PyViz::GetPacketDropSamples () const
  {
    NS_LOG_DEBUG ("GetPacketDropSamples BEGIN");
    PacketDropSampleList list;
    for (std::map<Ptr<Node>, uint32_t>::const_iterator
         iter = m_packetDrops.begin ();
         iter !=  m_packetDrops.end ();
         iter++)
      {
        PacketDropSample sample;
        sample.transmitter = iter->first;
        sample.bytes = iter->second;
        NS_LOG_DEBUG ("in " << sample.transmitter->GetId ()
                            << ": " << sample.bytes << " bytes dropped.");
        list.push_back (sample);
      }
    NS_LOG_DEBUG ("GetPacketDropSamples END");
    return list;
  }
  
  void
  PyViz::SetNodesOfInterest (std::set<uint32_t> nodes)
  {
    m_nodesOfInterest = nodes;
  }
  
  std::vector<PyViz::NodeStatistics>
  PyViz::GetNodesStatistics () const
  {
    std::vector<PyViz::NodeStatistics> retval;
    for (std::map<uint32_t, std::vector<NetDeviceStatistics> >::const_iterator iter = m_nodesStatistics.begin ();
         iter != m_nodesStatistics.end (); iter++)
      {
        NodeStatistics stats = { iter->first, iter->second };
        retval.push_back (stats);
      }
    return retval;
  }
  
  
  PyViz::LastPacketsSample
  PyViz::GetLastPackets (uint32_t nodeId) const
  {
    NS_LOG_DEBUG ("GetLastPackets: " << nodeId);
  
    std::map<uint32_t, LastPacketsSample>::const_iterator
      iter = m_lastPackets.find (nodeId);
    if (iter != m_lastPackets.end ())
      {
        return iter->second;
      }
    else
      {
        return LastPacketsSample ();
      }
  }
  
  
  
  
  
  
  namespace
  {
  // Adapted from http://en.wikipedia.org/w/index.php?title=Line_clipping&oldid=248609574
  class FastClipping
  {
  public:
    struct Vector2
    {
      double x;
      double y;
    };
  
    Vector2 m_clipMin, m_clipMax;
  
    struct Line
    {
      Vector2 start, end;
      double dx, dy;
    };
  
  private:
  
    void ClipStartTop (Line &line)
    {
      line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
      line.start.y = m_clipMin.y;
    }
   
    void ClipStartBottom (Line &line)
    {
      line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
      line.start.y = m_clipMax.y;
    }
   
    void ClipStartRight (Line &line)
    {
      line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
      line.start.x = m_clipMax.x;
    }
   
    void ClipStartLeft (Line &line)
    {
      line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
      line.start.x = m_clipMin.x;
    }
   
    void ClipEndTop (Line &line)
    {
      line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
      line.end.y = m_clipMin.y;
    }
  
    void ClipEndBottom (Line &line) {
      line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
      line.end.y = m_clipMax.y;
    }
   
    void ClipEndRight (Line &line)
    {
      line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
      line.end.x = m_clipMax.x;
    }
   
    void ClipEndLeft (Line &line)
    {
      line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
      line.end.x = m_clipMin.x;
    }
  
  public:
    FastClipping (Vector2 clipMin, Vector2 clipMax)
      : m_clipMin (clipMin), m_clipMax (clipMax)
    {
    }
  
   
    bool ClipLine (Line &line)
    {
      uint8_t lineCode = 0;
   
      if (line.end.y < m_clipMin.y)
        lineCode |= 8;
      else if (line.end.y > m_clipMax.y)
        lineCode |= 4;
   
      if (line.end.x > m_clipMax.x)
        lineCode |= 2;
      else if (line.end.x < m_clipMin.x)
        lineCode |= 1;
   
      if (line.start.y < m_clipMin.y)
        lineCode |= 128;
      else if (line.start.y > m_clipMax.y)
        lineCode |= 64;
   
      if (line.start.x > m_clipMax.x)
        lineCode |= 32;
      else if (line.start.x < m_clipMin.x)
        lineCode |= 16;
   
      // 9 - 8 - A
      // |   |   |
      // 1 - 0 - 2
      // |   |   |
      // 5 - 4 - 6
      switch (lineCode)
        {
        // center
        case 0x00:
          return true;
   
        case 0x01:
          ClipEndLeft (line);
          return true;
   
        case 0x02:
          ClipEndRight (line);
          return true;
   
        case 0x04:
          ClipEndBottom (line);
          return true;
   
        case 0x05:
          ClipEndLeft (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        case 0x06:
          ClipEndRight (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        case 0x08:
          ClipEndTop (line);
          return true;
   
        case 0x09:
          ClipEndLeft (line);
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          return true;
   
        case 0x0A:
          ClipEndRight (line);
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          return true;
   
        // left
        case 0x10:
          ClipStartLeft (line);
          return true;
   
        case 0x12:
          ClipStartLeft (line);
          ClipEndRight (line);
          return true;
   
        case 0x14:
          ClipStartLeft (line);
          if (line.start.y > m_clipMax.y)
            return false;
          ClipEndBottom (line);
          return true;
   
        case 0x16:
          ClipStartLeft (line);
          if (line.start.y > m_clipMax.y)
            return false;
          ClipEndBottom (line);
          if (line.end.x > m_clipMax.x)
            ClipEndRight (line);
          return true;
   
        case 0x18:
          ClipStartLeft (line);
          if (line.start.y < m_clipMin.y)
            return false;
          ClipEndTop (line);
          return true;
   
        case 0x1A:
          ClipStartLeft (line);
          if (line.start.y < m_clipMin.y)
            return false;
          ClipEndTop (line);
          if (line.end.x > m_clipMax.x)
            ClipEndRight (line);
          return true;
   
        // right
        case 0x20:
          ClipStartRight (line);
          return true;
   
        case 0x21:
          ClipStartRight (line);
          ClipEndLeft (line);
          return true;
   
        case 0x24:
          ClipStartRight (line);
          if (line.start.y > m_clipMax.y)
            return false;
          ClipEndBottom (line);
          return true;
   
        case 0x25:
          ClipStartRight (line);
          if (line.start.y > m_clipMax.y)
            return false;
          ClipEndBottom (line);
          if (line.end.x < m_clipMin.x)
            ClipEndLeft (line);
          return true;
   
        case 0x28:
          ClipStartRight (line);
          if (line.start.y < m_clipMin.y)
            return false;
          ClipEndTop (line);
          return true;
   
        case 0x29:
          ClipStartRight (line);
          if (line.start.y < m_clipMin.y)
            return false;
          ClipEndTop (line);
          if (line.end.x < m_clipMin.x)
            ClipEndLeft (line);
          return true;
   
        // bottom
        case 0x40:
          ClipStartBottom (line);
          return true;
   
        case 0x41:
          ClipStartBottom (line);
          if (line.start.x < m_clipMin.x)
            return false;
          ClipEndLeft (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        case 0x42:
          ClipStartBottom (line);
          if (line.start.x > m_clipMax.x)
            return false;
          ClipEndRight (line);
          return true;
   
        case 0x48:
          ClipStartBottom (line);
          ClipEndTop (line);
          return true;
   
        case 0x49:
          ClipStartBottom (line);
          if (line.start.x < m_clipMin.x)
            return false;
          ClipEndLeft (line);
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          return true;
   
        case 0x4A:
          ClipStartBottom (line);
          if (line.start.x > m_clipMax.x)
            return false;
          ClipEndRight (line);
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          return true;
   
        // bottom-left
        case 0x50:
          ClipStartLeft (line);
          if (line.start.y > m_clipMax.y)
            ClipStartBottom (line);
          return true;
   
        case 0x52:
          ClipEndRight (line);
          if (line.end.y > m_clipMax.y)
            return false;
          ClipStartBottom (line);
          if (line.start.x < m_clipMin.x)
            ClipStartLeft (line);
          return true;
   
        case 0x58:
          ClipEndTop (line);
          if (line.end.x < m_clipMin.x)
            return false;
          ClipStartBottom (line);
          if (line.start.x < m_clipMin.x)
            ClipStartLeft (line);
          return true;
   
        case 0x5A:
          ClipStartLeft (line);
          if (line.start.y < m_clipMin.y)
            return false;
          ClipEndRight (line);
          if (line.end.y > m_clipMax.y)
            return false;
          if (line.start.y > m_clipMax.y)
            ClipStartBottom (line);
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          return true;
   
        // bottom-right
        case 0x60:
          ClipStartRight (line);
          if (line.start.y > m_clipMax.y)
            ClipStartBottom (line);
          return true;
   
        case 0x61:
          ClipEndLeft (line);
          if (line.end.y > m_clipMax.y)
            return false;
          ClipStartBottom (line);
          if (line.start.x > m_clipMax.x)
            ClipStartRight (line);
          return true;
   
        case 0x68:
          ClipEndTop (line);
          if (line.end.x > m_clipMax.x)
            return false;
          ClipStartRight (line);
          if (line.start.y > m_clipMax.y)
            ClipStartBottom (line);
          return true;
   
        case 0x69:
          ClipEndLeft (line);
          if (line.end.y > m_clipMax.y)
            return false;
          ClipStartRight (line);
          if (line.start.y < m_clipMin.y)
            return false;
          if (line.end.y < m_clipMin.y)
            ClipEndTop (line);
          if (line.start.y > m_clipMax.y)
            ClipStartBottom (line);
          return true;
   
        // top
        case 0x80:
          ClipStartTop (line);
          return true;
   
        case 0x81:
          ClipStartTop (line);
          if (line.start.x < m_clipMin.x)
            return false;
          ClipEndLeft (line);
          return true;
   
        case 0x82:
          ClipStartTop (line);
          if (line.start.x > m_clipMax.x)
            return false;
          ClipEndRight (line);
          return true;
   
        case 0x84:
          ClipStartTop (line);
          ClipEndBottom (line);
          return true;
   
        case 0x85:
          ClipStartTop (line);
          if (line.start.x < m_clipMin.x)
            return false;
          ClipEndLeft (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        case 0x86:
          ClipStartTop (line);
          if (line.start.x > m_clipMax.x)
            return false;
          ClipEndRight (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        // top-left
        case 0x90:
          ClipStartLeft (line);
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          return true;
   
        case 0x92:
          ClipEndRight (line);
          if (line.end.y < m_clipMin.y)
            return false;
          ClipStartTop (line);
          if (line.start.x < m_clipMin.x)
            ClipStartLeft (line);
          return true;
   
        case 0x94:
          ClipEndBottom (line);
          if (line.end.x < m_clipMin.x)
            return false;
          ClipStartLeft (line);
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          return true;
   
        case 0x96:
          ClipStartLeft (line);
          if (line.start.y > m_clipMax.y)
            return false;
          ClipEndRight (line);
          if (line.end.y < m_clipMin.y)
            return false;
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          return true;
   
        // top-right
        case 0xA0:
          ClipStartRight (line);
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          return true;
   
        case 0xA1:
          ClipEndLeft (line);
          if (line.end.y < m_clipMin.y)
            return false;
          ClipStartTop (line);
          if (line.start.x > m_clipMax.x)
            ClipStartRight (line);
          return true;
   
        case 0xA4:
          ClipEndBottom (line);
          if (line.end.x > m_clipMax.x)
            return false;
          ClipStartRight (line);
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          return true;
   
        case 0xA5:
          ClipEndLeft (line);
          if (line.end.y < m_clipMin.y)
            return false;
          ClipStartRight (line);
          if (line.start.y > m_clipMax.y)
            return false;
          if (line.end.y > m_clipMax.y)
            ClipEndBottom (line);
          if (line.start.y < m_clipMin.y)
            ClipStartTop (line);
          return true;
        }
   
      return false;
    }
  };
  }
  
  void
  PyViz::LineClipping (double boundsX1, double boundsY1, double boundsX2, double boundsY2,
                       double &lineX1, double &lineY1, double &lineX2, double &lineY2)
  {
    FastClipping::Vector2 clipMin = { boundsX1, boundsY1}, clipMax = { boundsX2, boundsY2};
    FastClipping::Line line = { { lineX1, lineY1 }, { lineX2, lineY2 }, (lineX2-lineX1), (lineY2-lineY1) };
  
    FastClipping clipper (clipMin, clipMax);
    clipper.ClipLine (line);
    lineX1 = line.start.x;
    lineX2 = line.end.x;
    lineY1 = line.start.y;
    lineY2 = line.end.y;
  }
  
  
  }
  
  