//===========================================================================
//  @(#) $Name: arts++-1-1-a12 $
//  @(#) $Id: ArtsIpPathData.cc,v 1.9 2004/05/26 20:02:38 youngh Exp $
//===========================================================================
//  Copyright Notice
//
//  By accessing this software, arts++, you are duly informed
//  of and agree to be bound by the conditions described below in this
//  notice:
//
//  This software product, arts++, is developed by Daniel W. McRobb, and
//  copyrighted(C) 1998 by the University of California, San Diego
//  (UCSD), with all rights reserved.  UCSD administers the CAIDA grant,
//  NCR-9711092, under which part of this code was developed.
//
//  There is no charge for arts++ software.  You can redistribute it
//  and/or modify it under the terms of the GNU Lesser General Public
//  License, Version 2.1, February 1999, which is incorporated by
//  reference herein.
//
//  arts++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF
//  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use
//  of it will not infringe on any third party's intellectual
//  property rights.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with arts++.  Copies can also be obtained from:
//
//    http://www.gnu.org/copyleft/lesser.html
//
//  or by writing to:
//
//  Free Software Foundation, Inc.
//  59 Temple Place, Suite 330
//  Boston, MA 02111-1307
//  USA
//
//  Or contact:
//
//    info@caida.org
//===========================================================================

extern "C" {
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
}

#include <string>
#include <algorithm>

#include "Arts.hh"
#include "ArtsIpPathData.hh"

using namespace std;

static const std::string rcsid = "@(#) $Name: arts++-1-1-a12 $ $Id: ArtsIpPathData.cc,v 1.9 2004/05/26 20:02:38 youngh Exp $";
//------------------------------------------------------------------------
//     ArtsIpPathData::ArtsIpPathData(ipv4addr_t src, ipv4addr_t dst)
//------------------------------------------------------------------------
ArtsIpPathData::ArtsIpPathData(ipv4addr_t src, ipv4addr_t dst)
{
  this->Clear();
  
  this->_src = src;
  this->_dst = dst;

  #ifndef NDEBUG
    ++this->_numObjects;
  #endif
}

//-------------------------------------------------------------------------
//  ArtsIpPathData::ArtsIpPathData(const ArtsIpPathData & artsIpPathData)  
//.........................................................................
//                                                                         
//-------------------------------------------------------------------------
ArtsIpPathData::ArtsIpPathData(const ArtsIpPathData & artsIpPathData)
{
  _src = artsIpPathData._src;
  _dst = artsIpPathData._dst;
  _listId = artsIpPathData._listId;
  _cycleId = artsIpPathData._cycleId;
  _rtt = artsIpPathData._rtt;
  _hopDistance = artsIpPathData._hopDistance;
  _destinationReplied = artsIpPathData._destinationReplied;
  _replyTtl = artsIpPathData._replyTtl;
  _numHops = artsIpPathData._numHops;
  _haltReason = artsIpPathData._haltReason;
  _haltReasonData = artsIpPathData._haltReasonData;
  _path = artsIpPathData._path;

  #ifndef NDEBUG
    ++_numObjects;
  #endif
}

//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
ArtsIpPathData::ArtsIpPathData()
{
  this->Clear();

  #ifndef NDEBUG
    ++this->_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//     uint8_t ArtsIpPathData::LoopLength() const 
//............................................................................
//  
//----------------------------------------------------------------------------
uint8_t ArtsIpPathData::LoopLength() const
{
    return (_haltReason == k_loopDetected) ? _haltReasonData : 0;
}

//----------------------------------------------------------------------------
//     uint8_t ArtsIpPathData::LoopLength(uint8_t loopLength) 
//............................................................................
//  
//----------------------------------------------------------------------------
uint8_t ArtsIpPathData::LoopLength(uint8_t loopLength)
{
    _haltReason = k_loopDetected;
    _haltReasonData = loopLength;
    return _haltReasonData;
}

//----------------------------------------------------------------------------
//     uint8_t ArtsIpPathData::GapLimit() const 
//............................................................................
//  
//----------------------------------------------------------------------------
uint8_t ArtsIpPathData::GapLimit() const
{
    return (_haltReason == k_gapLimitExceeded) ? _haltReasonData : 0;
}

//----------------------------------------------------------------------------
//     uint8_t ArtsIpPathData::GapLimit(uint8_t gapLimit) 
//............................................................................
//  
//----------------------------------------------------------------------------
uint8_t ArtsIpPathData::GapLimit(uint8_t gapLimit)
{
    _haltReason = k_gapLimitExceeded;
    _haltReasonData = gapLimit;
    return _haltReasonData;
}

uint8_t ArtsIpPathData::IcmpCode() const
{
    return (_haltReason == k_icmpUnreachable) ? _haltReasonData : 0;
}

uint8_t ArtsIpPathData::IcmpCode(uint8_t icmpCode)
{
    _haltReason = k_icmpUnreachable;
    _haltReasonData = icmpCode;
    return _haltReasonData;
}

//----------------------------------------------------------------------------
//     void ArtsIpPathData::Clear() 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsIpPathData::Clear()
{
  _src = 0;
  _dst = 0;
  _listId = 0;
  _cycleId = 0;
  _hopDistance = 0;
  _destinationReplied = 0;
  _numHops = 0;
  _haltReason = k_noHalt;
  _haltReasonData = 0;
  _replyTtl = 0;
  
  return;
}

//------------------------------------------------------------------------
//                      ArtsIpPathData::~ArtsIpPathData()
//------------------------------------------------------------------------
ArtsIpPathData::~ArtsIpPathData()
{
  #ifndef NDEBUG
    --this->_numObjects;
  #endif
}

uint32_t ArtsIpPathData::Length(uint8_t version, uint8_t flags) const
{
  uint32_t len =  sizeof(_src) + sizeof(_dst) + sizeof(_hopDistance) +
		    sizeof(_numHops);

  if (version >= 3) {
    len += sizeof(_listId) + sizeof(_cycleId);
  }

  if (version >= 2) {
    len += sizeof(_rtt);
  } else {
    len += 2*sizeof(uint32_t);
  }

  if (version >= 1) {
    // Only version 1 has conditional reason codes.
    if (version != 1 || _destinationReplied) {
      len += sizeof(_haltReason) + sizeof(_haltReasonData);
    }
  }
  if (version >= 2) {
    len += sizeof(_replyTtl);
  }
  
  vector<ArtsIpPathEntry>::const_iterator  pathEntry;
  for (pathEntry = this->Path().begin(); pathEntry != this->Path().end();
       pathEntry++) {
    len += pathEntry->Length(version, flags);
  }
  return(len);
}

ostream& ArtsIpPathData::write(ostream& os, uint8_t version,
				uint8_t flags)
{
  uint32_t  timeDatum;
  uint8_t   repliedAndNumHops;

  os.write((char*)&this->_src,sizeof(this->_src));
  os.write((char*)&this->_dst,sizeof(this->_dst));

  if (version >= 3) {
    uint32_t id = htonl(_listId);
    os.write((char*)&id,sizeof(id));

    id = htonl(_cycleId);
    os.write((char*)&id,sizeof(id));
  }

  if (version >= 2) {
    timeDatum = htonl(_rtt);
  } else {
    timeDatum = htonl(_rtt / 1000000);
    os.write((char*)&timeDatum,sizeof(timeDatum));
    timeDatum = htonl(_rtt % 1000000);
  }
  os.write((char*)&timeDatum,sizeof(timeDatum));

  os.write((char*)&this->_hopDistance,sizeof(this->_hopDistance));
  
  repliedAndNumHops = (this->_destinationReplied << 7) | this->_numHops;
  os.write((char*)&repliedAndNumHops,sizeof(repliedAndNumHops));

  if (version >= 1) {
    // Only version 1 has conditional reason codes.
    if (version != 1 || _destinationReplied) {
      os.write((char*)&_haltReason, sizeof(_haltReason));
      os.write((char*)&_haltReasonData, sizeof(_haltReasonData));
    }
  }
  if (version >= 2) {
    os.write((char*)&_replyTtl, sizeof(_replyTtl));
  }

  //  sort by hop number
  sort(this->_path.begin(),this->_path.end(),less<ArtsIpPathEntry>());

  //  write each hop
  vector<ArtsIpPathEntry>::const_iterator  pathEntry;
  for (pathEntry = this->_path.begin(); pathEntry != this->_path.end();
       pathEntry++) {
    pathEntry->write(os,version, flags);
  }
  return(os);
}

int ArtsIpPathData::write(int fd, uint8_t version, uint8_t flags)
{
  uint32_t  timeDatum;
  uint8_t   repliedAndNumHops;
  int       rc;
  int       bytesWritten = 0;
  
  rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_src,sizeof(this->_src));
  if (rc != sizeof(this->_src)) {
    return(-1);
  }
  bytesWritten += rc;
  
  rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_dst,sizeof(this->_dst));
  if (rc != sizeof(this->_dst)) {
    return(-1);
  }
  bytesWritten += rc;

  if (version >= 3) {
    uint32_t id = htonl(_listId);
    rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&id,sizeof(id));
    if (rc != sizeof(id)) {
      return(-1);
    }
    bytesWritten += rc;

    id = htonl(_cycleId);
    rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&id,sizeof(id));
    if (rc != sizeof(id)) {
      return(-1);
    }
    bytesWritten += rc;
  }

  if (version >= 2) {
    timeDatum = htonl(_rtt);
  } else {
    timeDatum = htonl(_rtt / 1000000);
    rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&timeDatum,sizeof(timeDatum));
    if (rc != sizeof(timeDatum)) {
      return(-1);
    }
    bytesWritten += rc;
    timeDatum = htonl(_rtt % 1000000);
  }
  rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&timeDatum,sizeof(timeDatum));
  if (rc != sizeof(timeDatum)) {
    return(-1);
  }
  bytesWritten += rc;
  
  rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_hopDistance,
                                           sizeof(this->_hopDistance));
  if (rc != sizeof(this->_hopDistance)) {
    return(-1);
  }
  bytesWritten += rc;
  
  repliedAndNumHops = (this->_destinationReplied << 7) | this->_numHops;
  rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&repliedAndNumHops,
                                           sizeof(repliedAndNumHops));
  if (rc != sizeof(repliedAndNumHops)) {
    return(-1);
  }
  bytesWritten += rc;

  if (version >= 1) {
    // Only version 1 has conditional reason codes.
    if (version != 1 || _destinationReplied) {
      rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_haltReason,
                                            sizeof(_haltReason));
      if (rc != sizeof(_haltReason))
        return(-1);
      bytesWritten += rc;

      rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_haltReasonData,
                                               sizeof(_haltReasonData));
      if (rc != sizeof(_haltReasonData))
        return(-1);
      bytesWritten += rc;
    }
  }
  if (version >= 2) {
    rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_replyTtl, sizeof(_replyTtl));
    if (rc != sizeof(_replyTtl))
      return(-1);
    bytesWritten += rc;
  }
  
  //  sort by hop number
  sort(this->_path.begin(),this->_path.end(),less<ArtsIpPathEntry>());

  //  write each hop
  vector<ArtsIpPathEntry>::const_iterator  pathEntry;
  for (pathEntry = this->_path.begin(); pathEntry != this->_path.end();
       pathEntry++) {
    if ((rc = pathEntry->write(fd,version, flags)) < 0) {
      return(-1);
    }
    bytesWritten += rc;
  }
  return(bytesWritten);
}

istream& ArtsIpPathData::read(istream& is, uint8_t version,
				uint8_t flags)
{
  uint32_t  timeDatum;
  uint8_t   repliedAndNumHops;
  size_t    hopNum;
  
  is.read((char*)&this->_src,sizeof(this->_src));
  is.read((char*)&this->_dst,sizeof(this->_dst));

  if (version >= 3) {
    uint32_t id;

    is.read((char*)&id,sizeof(id));
    _listId = ntohl(id);

    is.read((char*)&id,sizeof(id));
    _cycleId = ntohl(id);
  }

  is.read((char*)&timeDatum,sizeof(timeDatum));
  if (version >= 2) {
    _rtt = ntohl(timeDatum);
  } else {
    _rtt = ntohl(timeDatum) * 1000000;
    is.read((char*)&timeDatum,sizeof(timeDatum));
    _rtt += ntohl(timeDatum);
  }

  is.read((char*)&this->_hopDistance,sizeof(this->_hopDistance));
  
  is.read((char*)&repliedAndNumHops,sizeof(repliedAndNumHops));
  this->_destinationReplied = repliedAndNumHops >> 7;
  this->_numHops = repliedAndNumHops & 0x7f;

  if (version >= 1) {
    // Only version 1 has conditional reason codes.
    if (version != 1 || _destinationReplied) {
      is.read((char*)&_haltReason, sizeof(_haltReason));
      is.read((char*)&_haltReasonData, sizeof(_haltReasonData));
    }
  }
  if (version >= 2) {
    is.read((char*)&_replyTtl, sizeof(_replyTtl));
  }
  
  if (this->_path.size() > 0) {
    this->_path.erase(this->_path.begin(),this->_path.end());
  }
  
  this->_path.reserve(this->_numHops);

  //  read in contents for each hop

  ArtsIpPathEntry  pathEntry;
  
  for (hopNum = 0; hopNum < this->_numHops; hopNum++) {
    pathEntry.read(is,version, flags);
    this->_path.push_back(pathEntry);
  }

  assert(_numHops == _path.size());
  
  return(is);
}

int ArtsIpPathData::read(int fd, uint8_t version, uint8_t flags)
{
  uint32_t  timeDatum;
  uint8_t   repliedAndNumHops;
  size_t    hopNum;
  int       rc;
  int       bytesRead = 0;
  
  rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_src,sizeof(this->_src));
  if (rc <= 0)
    return(rc);
  bytesRead += rc;
  rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_dst,sizeof(this->_dst));
  if (rc <= 0)
    return(rc);
  bytesRead += rc;
  
  if (version >= 3) {
    uint32_t id;

    rc = g_ArtsLibInternal_Primitive.FdRead(fd,&id,sizeof(id));
    if (rc <= 0)
      return(rc);
    bytesRead += rc;
    _listId = ntohl(id);

    rc = g_ArtsLibInternal_Primitive.FdRead(fd,&id,sizeof(id));
    if (rc <= 0)
      return(rc);
    bytesRead += rc;
    _cycleId = ntohl(id);
  }

  rc = g_ArtsLibInternal_Primitive.FdRead(fd,&timeDatum,sizeof(timeDatum));
  if (rc <= 0)
    return(rc);
  bytesRead += rc;
  if (version >= 2) {
    _rtt = ntohl(timeDatum);
  } else {
    _rtt = ntohl(timeDatum) * 1000000;
    rc = g_ArtsLibInternal_Primitive.FdRead(fd,&timeDatum,sizeof(timeDatum));
    if (rc <= 0)
      return(rc);
    bytesRead += rc;
    _rtt += ntohl(timeDatum);
  }

  rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_hopDistance,
                                          sizeof(this->_hopDistance));
  if (rc <= 0)
    return(rc);
  bytesRead += rc;
  
  rc = g_ArtsLibInternal_Primitive.FdRead(fd,&repliedAndNumHops,
                                          sizeof(repliedAndNumHops));
  if (rc <= 0)
    return(rc);
  bytesRead += rc;
  this->_destinationReplied = repliedAndNumHops >> 7;
  this->_numHops = repliedAndNumHops & 0x7f;

  if (version >= 1) {
    // Only version 1 has conditional reason codes.
    if (version != 1 || _destinationReplied) {
      rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_haltReason,
                                              sizeof(_haltReason));
      if (rc <= 0)
        return(rc);
      bytesRead += rc;
      
      rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_haltReasonData,
                                              sizeof(_haltReasonData));
      if (rc <= 0)
        return(rc);
      bytesRead += rc;
    }
  }
  if (version >= 2) {
    rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_replyTtl, sizeof(_replyTtl));
    if (rc <= 0)
      return(rc);
    bytesRead += rc;
  }
  
  if (this->_path.size() > 0) {
    this->_path.erase(this->_path.begin(),this->_path.end());
  }
  
  this->_path.reserve(this->_numHops);

  //  read in contents for each hop

  ArtsIpPathEntry  pathEntry;
  
  for (hopNum = 0; hopNum < this->_numHops; hopNum++) {
    rc = pathEntry.read(fd,version, flags);
    if (rc <= 0)
      return(rc);
    bytesRead += rc;
    this->_path.push_back(pathEntry);
  }

  assert(_numHops == _path.size());
  
  return(bytesRead);
}

//-------------------------------------------------------------------------
//     bool                                  
//     ArtsIpPathData::
//     Distinguishable(const vector<ArtsIpPathEntry> & ipPath) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool
ArtsIpPathData::Distinguishable(const vector<ArtsIpPathEntry> & ipPath) const
{
  vector<ArtsIpPathEntry>::const_iterator  thisHopIter;
  vector<ArtsIpPathEntry>::const_iterator  inHopIter;

  for (thisHopIter = (*this)._path.begin();
       thisHopIter != (*this)._path.end(); thisHopIter++) {
    for (inHopIter = ipPath.begin();
         inHopIter != ipPath.end(); inHopIter++) {
      if ((*thisHopIter).HopNum() == (*inHopIter).HopNum()) {
        if ((*thisHopIter).IpAddr() != (*inHopIter).IpAddr()) {
          return(true);
        }
      }
    }
  }
  return(false);
}

//-------------------------------------------------------------------------
// bool                                  
// ArtsIpPathData::Distinguishable(const ArtsIpPathData & ipPathData) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool
ArtsIpPathData::Distinguishable(const ArtsIpPathData & ipPathData) const
{
  if (this->_src != ipPathData.Src() || this->_dst != ipPathData.Dst()) {
    //  different source or destination addresses
    return(true);
  }
  
  if (_destinationReplied && ipPathData.DestinationReplied()) {
    //  both paths claim to be complete; compare the hop distance
    if (this->_hopDistance != ipPathData.HopDistance()) {
      return(true);
    }
  }
  
  if (this->Distinguishable(ipPathData.Path())) {
    //  the hop vectors are distinguishable
    return(true);
  }
  return(false);
}

//-------------------------------------------------------------------------
//        vector<ipv4addr_t> * ArtsIpPathData::HopAddresses() const        
//.........................................................................
//  
//-------------------------------------------------------------------------
vector<ipv4addr_t> * ArtsIpPathData::HopAddresses() const
{
  vector<ipv4addr_t>  *newAddrVect = new vector<ipv4addr_t>;

  vector<ArtsIpPathEntry>::const_iterator  thisHopIter;
  for (thisHopIter = (*this)._path.begin();
       thisHopIter != (*this)._path.end(); thisHopIter++) {
    (*newAddrVect).push_back((*thisHopIter).IpAddr());
  }
  return(newAddrVect);
}

//-------------------------------------------------------------------------
//     bool                                  
//     ArtsIpPathData::
//     CommonHopAddresses(const vector<ArtsIpPathEntry> & ipPath, 
//                        vector<ipv4addr_t> & commonHopAddrs) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool
ArtsIpPathData::CommonHopAddresses(const vector<ArtsIpPathEntry> & ipPath,
                                   vector<ipv4addr_t> & commonHopAddrs) const
{
  vector<ArtsIpPathEntry>::const_iterator  thisHopIter;
  vector<ArtsIpPathEntry>::const_iterator  inHopIter;
  bool                                     rc = false;
  
  for (thisHopIter = (*this)._path.begin();
       thisHopIter != (*this)._path.end(); thisHopIter++) {
    for (inHopIter = ipPath.begin(); inHopIter != ipPath.end(); inHopIter++) {
      if ((*thisHopIter).IpAddr() == (*inHopIter).IpAddr()) {
        rc = true;
        if (find(commonHopAddrs.begin(),commonHopAddrs.end(),
                 (*thisHopIter).IpAddr()) == commonHopAddrs.end()) {
          commonHopAddrs.push_back((*thisHopIter).IpAddr());
        }
      }
    }
  }
  return(rc);
}

//-------------------------------------------------------------------------
//    bool                                  
//    ArtsIpPathData::                      
//    CommonHopAddresses(const vector<ipv4addr_t> & hopAddresses, 
//                       vector<ipv4addr_t> & commonHopAddresses) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool
ArtsIpPathData::
CommonHopAddresses(const vector<ipv4addr_t> & hopAddresses,
                   vector<ipv4addr_t> & commonHopAddresses) const
{
  vector<ArtsIpPathEntry>::const_iterator  thisHopIter;
  bool                                     rc;

  if (commonHopAddresses.size() > 0) {
    commonHopAddresses.erase(commonHopAddresses.begin(),
                             commonHopAddresses.end());
  }
  
  for (thisHopIter = (*this)._path.begin();
       thisHopIter != (*this)._path.end(); thisHopIter++) {
    if (find(hopAddresses.begin(),hopAddresses.end(),(*thisHopIter).IpAddr())
        != hopAddresses.end()) {
      rc = true;
      if (find(commonHopAddresses.begin(),commonHopAddresses.end(),
               (*thisHopIter).IpAddr()) == commonHopAddresses.end()) {
        commonHopAddresses.push_back((*thisHopIter).IpAddr());
      }
    }
  }
  return(rc);
}


//-------------------------------------------------------------------------
// void
// ArtsIpPathData::MergeWithPath(const vector<ArtsIpPathEntry> & ipPath,  
//                               vector<ArtsIpPathEntry> & mergedPath) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
void
ArtsIpPathData::MergeWithPath(const vector<ArtsIpPathEntry> & ipPath,
                              vector<ArtsIpPathEntry> & mergedPath) const
{
  vector<ArtsIpPathEntry>::iterator       unionIterBegin, unionIterEnd;
  ArtsIpPathEntryLessByHopNumber          hopsLess;
  
  mergedPath.reserve((*this)._path.size() + ipPath.size());
  unionIterBegin = mergedPath.begin();
  unionIterEnd = set_union((*this)._path.begin(),(*this)._path.end(),
                           ipPath.begin(),ipPath.end(),
                           unionIterBegin,hopsLess);
  mergedPath.insert(mergedPath.begin(),unionIterBegin,unionIterEnd);
  return;
}


//-------------------------------------------------------------------------
//   ArtsIpPathData &
//   ArtsIpPathData::operator = (const ArtsIpPathData & artsIpPathData)
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsIpPathData &
ArtsIpPathData::operator = (const ArtsIpPathData & artsIpPathData)
{
  this->_src = artsIpPathData._src;
  this->_dst = artsIpPathData._dst;
  this->_listId = artsIpPathData._listId;
  this->_cycleId = artsIpPathData._cycleId;
  this->_rtt = artsIpPathData._rtt;
  this->_hopDistance = artsIpPathData._hopDistance;
  this->_destinationReplied = artsIpPathData._destinationReplied;
  this->_numHops = artsIpPathData._numHops;
  this->_haltReason = artsIpPathData._haltReason;
  this->_haltReasonData = artsIpPathData._haltReasonData;
  this->_path = artsIpPathData._path;
  return(*this);
}

//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
ostream& operator << (ostream& os, const ArtsIpPathData & artsIpPathData)
{
  struct in_addr  inAddr;
  
  os << "IPPATH OBJECT DATA" << endl;
  inAddr.s_addr = artsIpPathData.Src();
  os << "\tSrc: " << setiosflags(ios::left) << setw(16) << inet_ntoa(inAddr)
     << setiosflags(ios::showbase) << " (" << setw(8) << hex
     << ntohl(artsIpPathData.Src()) << ")" << endl;
  inAddr.s_addr = artsIpPathData.Dst();
  os << "\tDst: " << setiosflags(ios::left) << setw(16) << inet_ntoa(inAddr)
     << " (" << setiosflags(ios::showbase) << setw(8) << hex
     << ntohl(artsIpPathData.Dst()) << ")" << dec << endl;
  os << "\tListId: " << artsIpPathData.ListId() << " (" << hex
     << artsIpPathData.ListId() << ")" << dec << endl;

  // Timestamp printing code copied from ArtsAttribute.cc::operator<<()
  // We should probably print in GMT, but convention seems to prefer local.
  time_t timestamp = artsIpPathData.CycleId();
  struct tm *Tm = localtime(&timestamp);
  os << setiosflags(ios::internal);
  os << "\tCycleID: " << setfill('0') << setw(2) << Tm->tm_mon+1 << "/"
     << setw(2) << Tm->tm_mday << "/" << setw(4) << Tm->tm_year+1900
     << " " << setw(2) << Tm->tm_hour << ":" << setw(2)
     << Tm->tm_min << ":" << setw(2) << Tm->tm_sec << " ("
     << hex << timestamp << ")" << dec << endl;
  os << setfill(' ');

  os << "\tRtt: " << (float)((artsIpPathData.Rtt().tv_sec * 1000.0 +
                              artsIpPathData.Rtt().tv_usec)/1000.0)
     << " ms" << endl;
  os << "\tHopDistance: " << dec << (int)artsIpPathData.HopDistance()
     << " (" << hex << (int)artsIpPathData.HopDistance() << ")" << endl;
  os << "\tDestinationReplied: ";
  if (artsIpPathData.DestinationReplied())
    os << "true\n";
  else {
    os << "false\n";
  }
  uint8_t reason = artsIpPathData.HaltReason();
  if (reason != ArtsIpPathData::k_noHalt) {
      os << "\tHaltReason: ";
      if (reason == ArtsIpPathData::k_icmpUnreachable) {
	  os << "ICMP unreachable, code: " << dec
		<< (unsigned int)artsIpPathData.IcmpCode() << hex << endl;
      } else if (reason == ArtsIpPathData::k_loopDetected) {
	  os << "loop detected, loop length: " << dec
		<< (unsigned int)artsIpPathData.LoopLength() << hex << endl;
      } else if (reason == ArtsIpPathData::k_gapLimitExceeded) {
	  os << "gap limit reached: " << dec
		<< (unsigned int)artsIpPathData.GapLimit() << hex << endl;
      }
  }
  if (artsIpPathData.ReplyTtl()) {
      os << "\tReply TTL: " << dec << (unsigned int)artsIpPathData.ReplyTtl()
	    << hex << endl;
  }
  os << endl;
  os << "\tNumHops: " << dec << (int)artsIpPathData.NumHops()
     << " (" << hex << (int)artsIpPathData.NumHops() << ")" << endl;

  assert(artsIpPathData.Path().size() == artsIpPathData.NumHops());

  vector<ArtsIpPathEntry>::const_iterator  pathEntry;
  for (pathEntry = artsIpPathData.Path().begin();
       pathEntry != artsIpPathData.Path().end();
       pathEntry++) {
    os << (*pathEntry);
  }
  
  return(os);
}



//AL:
//----------------------------------------------------------------------------
//     void ArtsIpPathData::AddHop(ipv4addr_t ipAddr, uint8_t hopNum, 
//                                 const struct timeval & rtt, uint8_t numTries) 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsIpPathData::AddHop(ipv4addr_t ipAddr, uint8_t hopNum,
                            const struct timeval & rtt, uint8_t numTries)
{
  ArtsIpPathEntry  pathEntry(ipAddr,hopNum);
  pathEntry.Rtt(rtt);
  pathEntry.NumTries(numTries);
  this->_path.push_back(pathEntry);
  this->NumHops(this->Path().size());
}

uint32_t ArtsIpPathData::_numObjects = 0;
