//===========================================================================
//  $Name: arts++-1-1-a12 $
//  $Id: ArtsRttTimeSeriesTableData.cc,v 1.3 2004/06/23 16:57:27 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 "artslocal.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <assert.h>
}

#include <string>
#include <algorithm>

#ifdef HAVE_IOMANIP
  #include <iomanip>
#else
  #include <iomanip.h>
#endif

#include "ArtsRttTimeSeriesTableData.hh"
#include "ArtsPrimitive.hh"

using namespace std;

static const std::string rcsid = "@(#) $Name: arts++-1-1-a12 $ $Id: ArtsRttTimeSeriesTableData.cc,v 1.3 2004/06/23 16:57:27 youngh Exp $";

//----------------------------------------------------------------------------
//        ArtsRttTimeSeriesTableEntry::ArtsRttTimeSeriesTableEntry()
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableEntry::ArtsRttTimeSeriesTableEntry()
{
  this->_rtt               = 0;
  this->_timestamp.tv_sec  = 0;
  this->_timestamp.tv_usec = 0;
  
  #ifndef NDEBUG
    ++_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//  ArtsRttTimeSeriesTableEntry::
//  ArtsRttTimeSeriesTableEntry(const ArtsRttTimeSeriesTableEntry & rttEntry) 
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableEntry::
ArtsRttTimeSeriesTableEntry(const ArtsRttTimeSeriesTableEntry & rttEntry)
{
  this->Rtt(rttEntry.Rtt());
  this->Timestamp(rttEntry.Timestamp());

  #ifndef NDEBUG
    ++_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//       ArtsRttTimeSeriesTableEntry::~ArtsRttTimeSeriesTableEntry()       
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableEntry::~ArtsRttTimeSeriesTableEntry()
{
  #ifndef NDEBUG
    --_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//            uint32_t ArtsRttTimeSeriesTableEntry::Rtt() const            
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableEntry::Rtt() const
{
  return(this->_rtt);
}

//----------------------------------------------------------------------------
//         uint32_t ArtsRttTimeSeriesTableEntry::Rtt(uint32_t rtt)         
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableEntry::Rtt(uint32_t rtt)
{
  this->_rtt = rtt;
  return(this->_rtt);
}

//----------------------------------------------------------------------------
//  const struct timeval & ArtsRttTimeSeriesTableEntry::Timestamp() const  
//............................................................................
//  
//----------------------------------------------------------------------------
const struct timeval & ArtsRttTimeSeriesTableEntry::Timestamp() const
{
  return(this->_timestamp);
}

//----------------------------------------------------------------------------
//  const struct timeval &                         
//  ArtsRttTimeSeriesTableEntry::Timestamp(const struct timeval & timestamp) 
//............................................................................
//  
//----------------------------------------------------------------------------
const struct timeval &
ArtsRttTimeSeriesTableEntry::Timestamp(const struct timeval & timestamp)
{
  this->_timestamp.tv_sec = timestamp.tv_sec;
  this->_timestamp.tv_usec = timestamp.tv_usec;
  return(this->_timestamp);
}

//----------------------------------------------------------------------------
//         ArtsRttTimeSeriesTableEntry &                      
//         ArtsRttTimeSeriesTableEntry::
//         operator = (const ArtsRttTimeSeriesTableEntry & rttEntry) 
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableEntry::
operator = (const ArtsRttTimeSeriesTableEntry & rttEntry)
{
  this->Rtt(rttEntry.Rtt());
  this->Timestamp(rttEntry.Timestamp());
  return(*this);
}

static uint8_t BytesNeededForUint32(uint32_t value)
{
  if (value > 0x00FFFFFF)
    return(4);
  else if (value > 0x0000FFFF)
    return(3);
  else if (value > 0x000000FF)
    return(2);
  else 
    return(1);
}

//----------------------------------------------------------------------------
//  ostream & ArtsRttTimeSeriesTableEntry::write(ostream & os, 
//                                               uint32_t timeBase, 
//                                               uint32_t prevSecsOffset, 
//                                               uint8_t version) const
//............................................................................
//  
//----------------------------------------------------------------------------
ostream & ArtsRttTimeSeriesTableEntry::write(ostream & os,
                                             uint32_t timeBase,
                                             uint32_t prevSecsOffset,
                                             uint8_t version) const
{
  uint8_t         rleFlags = 0;
  uint8_t         timestampSecsLength = 0;
  uint8_t         timestampUsecsLength = 0;
  uint8_t         rttLength = 0;
  uint32_t        timestampSecsOffset;
  
  if (this->_rtt != k_droppedPacketRtt) {
    rttLength = BytesNeededForUint32(this->_rtt);
    rleFlags |= ((rttLength - 1) << 4);
  }
  else {
    rleFlags |= 0x80;
  }

  timestampSecsOffset = this->_timestamp.tv_sec - timeBase;
  if (timestampSecsOffset != prevSecsOffset) {
    rleFlags |= 0x40;
    timestampSecsLength = BytesNeededForUint32(timestampSecsOffset);
    rleFlags |= ((timestampSecsLength - 1) << 2);
  }

  timestampUsecsLength = BytesNeededForUint32(this->_timestamp.tv_usec);
  rleFlags |= (timestampUsecsLength - 1);
  os.write((char*)&rleFlags,sizeof(rleFlags));
  if (rttLength > 0)
    g_ArtsLibInternal_Primitive.WriteUint32(os,this->_rtt,rttLength);
  if (timestampSecsLength > 0)
    g_ArtsLibInternal_Primitive.WriteUint32(os,timestampSecsOffset,
                                            timestampSecsLength);
  g_ArtsLibInternal_Primitive.WriteUint32(os,this->_timestamp.tv_usec,
                                          timestampUsecsLength);
  return(os);
}

//----------------------------------------------------------------------------
//     int ArtsRttTimeSeriesTableEntry::write(int fd, 
//                                            uint32_t timeBase, 
//                                            uint32_t prevSecsOffset, 
//                                            uint8_t version) const 
//............................................................................
//  
//----------------------------------------------------------------------------
int ArtsRttTimeSeriesTableEntry::write(int fd,
                                       uint32_t timeBase,
                                       uint32_t prevSecsOffset,
                                       uint8_t version) const
{
  uint8_t         rleFlags = 0;
  uint8_t         timestampSecsLength = 0;
  uint8_t         timestampUsecsLength = 0;
  uint8_t         rttLength = 0;
  uint32_t        timestampSecsOffset;
  
  if (this->_rtt != k_droppedPacketRtt) {
    rttLength = BytesNeededForUint32(this->_rtt);
    rleFlags |= ((rttLength - 1) << 4);
  }
  else {
    rleFlags |= 0x80;
  }

  timestampSecsOffset = this->_timestamp.tv_sec - timeBase;
  if (timestampSecsOffset != prevSecsOffset) {
    rleFlags |= 0x40;
    timestampSecsLength = BytesNeededForUint32(timestampSecsOffset);
    rleFlags |= ((timestampSecsLength - 1) << 2);
  }

  timestampUsecsLength = BytesNeededForUint32(this->_timestamp.tv_usec);
  rleFlags |= (timestampUsecsLength - 1);
  int rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&rleFlags,sizeof(rleFlags));
  if (rc < sizeof(rleFlags))
    return(-1);
  int bytesWritten = rc;
  
  if (rttLength > 0) {
    rc = g_ArtsLibInternal_Primitive.WriteUint32(fd,this->_rtt,rttLength);
    if (rc < rttLength)
      return(-1);
    bytesWritten += rc;
  }
  
  if (timestampSecsLength > 0) {
    rc = g_ArtsLibInternal_Primitive.WriteUint32(fd,timestampSecsOffset,
                                                 timestampSecsLength);
    if (rc < timestampSecsLength)
      return(-1);
    bytesWritten += rc;
  }
  
  rc = g_ArtsLibInternal_Primitive.WriteUint32(fd,this->_timestamp.tv_usec,
                                               timestampUsecsLength);
  if (rc < timestampUsecsLength)
    return(-1);
  bytesWritten += rc;
  
  return(bytesWritten);
}

//----------------------------------------------------------------------------
//  uint32_t ArtsRttTimeSeriesTableEntry::Length(uint32_t timeBase, 
//                                               uint32_t prevSecsOffset, 
//                                               uint8_t version) const
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableEntry::Length(uint32_t timeBase,
                                             uint32_t prevSecsOffset,
                                             uint8_t version) const
{
  uint32_t length = sizeof(uint8_t);  // always have rleFlags
  if (this->_rtt != k_droppedPacketRtt) {
    length += BytesNeededForUint32(this->_rtt);
  }

  uint32_t timestampSecsOffset = this->_timestamp.tv_sec - timeBase;
  if (timestampSecsOffset != prevSecsOffset) {
    length += BytesNeededForUint32(timestampSecsOffset);
  }

  length += BytesNeededForUint32(this->_timestamp.tv_usec);

  return(length);
}

//----------------------------------------------------------------------------
//     istream & ArtsRttTimeSeriesTableEntry::read(istream & is, 
//                                                 uint32_t timeBase, 
//                                                 uint32_t prevSecsOffset, 
//                                                 uint8_t version) 
//............................................................................
//  
//----------------------------------------------------------------------------
istream & ArtsRttTimeSeriesTableEntry::read(istream & is,
                                            uint32_t timeBase,
                                            uint32_t prevSecsOffset,
                                            uint8_t version)
{
  uint8_t         rleFlags = 0;
  uint8_t         timestampUsecsLength = 0;
  uint32_t        timeVal;
  
  is.read((char*)&rleFlags,sizeof(rleFlags));

  if (rleFlags & 0x80) {
    //  it's a dropped packet; RTT length is 0
    this->_rtt = k_droppedPacketRtt;
  }
  else {
    uint8_t rttLength = ((rleFlags >> 4) & 0x03) + 1;
    g_ArtsLibInternal_Primitive.ReadUint32(is,this->_rtt,rttLength);
  }

  if (rleFlags & 0x40) {
    uint8_t timestampSecsLength = ((rleFlags >> 2) & 0x03) + 1;
    g_ArtsLibInternal_Primitive.ReadUint32(is,timeVal,
                                           timestampSecsLength);
    this->_timestamp.tv_sec = timeVal + timeBase;
  }
  else {
    this->_timestamp.tv_sec = timeBase + prevSecsOffset;
  }
  
  timestampUsecsLength = (rleFlags & 0x03) + 1;
  g_ArtsLibInternal_Primitive.ReadUint32(is,timeVal,
                                         timestampUsecsLength);
  this->_timestamp.tv_usec = timeVal;
  
  return(is);
}

//----------------------------------------------------------------------------
//     int ArtsRttTimeSeriesTableEntry::read(int fd, uint32_t timeBase, 
//                                           uint32_t prevSecsOffset, 
//                                           uint8_t version) 
//............................................................................
//  
//----------------------------------------------------------------------------
int ArtsRttTimeSeriesTableEntry::read(int fd, uint32_t timeBase,
                                      uint32_t prevSecsOffset,
                                      uint8_t version)
{
  uint8_t         rleFlags = 0;
  uint8_t         timestampUsecsLength = 0;
  uint32_t        timeVal;
  
  int rc = g_ArtsLibInternal_Primitive.FdRead(fd,&rleFlags,sizeof(rleFlags));
  if (rc < sizeof(rleFlags)) {
    return(-1);
  }
  
  int bytesRead = rc;
  
  if (rleFlags & 0x80) {
    //  it's a dropped packet; RTT length is 0
    this->_rtt = k_droppedPacketRtt;
  }
  else {
    uint8_t rttLength = ((rleFlags >> 4) & 0x03) + 1;
    rc = g_ArtsLibInternal_Primitive.ReadUint32(fd,this->_rtt,rttLength);
    if (rc < rttLength) {
      return(-1);
    }
    bytesRead += rc;
  }

  if (rleFlags & 0x40) {
    uint8_t timestampSecsLength = ((rleFlags >> 2) & 0x03) + 1;
    rc = g_ArtsLibInternal_Primitive.ReadUint32(fd,timeVal,
                                                timestampSecsLength);
    if (rc < timestampSecsLength) {
      return(-1);
    }
    bytesRead += rc;
    this->_timestamp.tv_sec = timeVal + timeBase;
  }
  else {
    this->_timestamp.tv_sec = timeBase + prevSecsOffset;
  }
  
  timestampUsecsLength = (rleFlags & 0x03) + 1;
  rc = g_ArtsLibInternal_Primitive.ReadUint32(fd,timeVal,
                                              timestampUsecsLength);
  if (rc < timestampUsecsLength) {
    return(-1);
  }
  bytesRead += rc;
  this->_timestamp.tv_usec = timeVal;
  
  return(bytesRead);
}

//----------------------------------------------------------------------------
//         bool ArtsRttTimeSeriesTableEntryGreaterRtt:: 
//         operator () (const ArtsRttTimeSeriesTableEntry & rttEntry1, 
//                      const ArtsRttTimeSeriesTableEntry & rttEntry2) 
//............................................................................
//  
//----------------------------------------------------------------------------
bool ArtsRttTimeSeriesTableEntryGreaterRtt::
operator () (const ArtsRttTimeSeriesTableEntry & rttEntry1,
             const ArtsRttTimeSeriesTableEntry & rttEntry2)
{
  if (rttEntry1.Rtt() == ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
    if (rttEntry2.Rtt() != ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      return(true);
    }
    return(false);
  }
  if (rttEntry2.Rtt() == ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
    return(false);
  }
  
  return(rttEntry1.Rtt() > rttEntry2.Rtt());
}

//----------------------------------------------------------------------------
//      bool ArtsRttTimeSeriesTableEntryLessRtt:: 
//      operator () (const ArtsRttTimeSeriesTableEntry & rttEntry1, 
//                   const ArtsRttTimeSeriesTableEntry & rttEntry2) 
//............................................................................
//  
//----------------------------------------------------------------------------
bool ArtsRttTimeSeriesTableEntryLessRtt::
operator () (const ArtsRttTimeSeriesTableEntry & rttEntry1,
             const ArtsRttTimeSeriesTableEntry & rttEntry2)
{
  if (rttEntry1.Rtt() == ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
    if (rttEntry2.Rtt() != ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      return(false);
    }
    return(true);
  }
  if (rttEntry2.Rtt() == ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
    return(true);
  }
  
  return(rttEntry1.Rtt() < rttEntry2.Rtt());
}

//----------------------------------------------------------------------------
//         ArtsRttTimeSeriesTableData::ArtsRttTimeSeriesTableData()        
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableData::ArtsRttTimeSeriesTableData()
{
  this->_timeBase = 0;
  
  #ifndef NDEBUG
    ++_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//  ArtsRttTimeSeriesTableData::
//  ArtsRttTimeSeriesTableData(const ArtsRttTimeSeriesTableData & rttTable) 
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableData::
ArtsRttTimeSeriesTableData(const ArtsRttTimeSeriesTableData & rttTable)
{
  this->_timeBase   = rttTable.TimeBase();
  this->_rttEntries = rttTable.RttEntries();
  
  #ifndef NDEBUG
    ++_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//        ArtsRttTimeSeriesTableData::~ArtsRttTimeSeriesTableData()        
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableData::~ArtsRttTimeSeriesTableData()
{
  this->_rttEntries.erase(this->_rttEntries.begin(),this->_rttEntries.end());
  
  #ifndef NDEBUG
    --_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//           ArtsRttTimeSeriesTableData &                      
//           ArtsRttTimeSeriesTableData::
//           operator = (const ArtsRttTimeSeriesTableData & rttTable) 
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsRttTimeSeriesTableData &
ArtsRttTimeSeriesTableData::
operator = (const ArtsRttTimeSeriesTableData & rttTable)
{
  this->_rttEntries = rttTable.RttEntries();
  return(*this);
}

//----------------------------------------------------------------------------
//          uint32_t ArtsRttTimeSeriesTableData::TimeBase() const 
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableData::TimeBase() const
{
  if (this->_rttEntries.size() > 0)
    if (this->_timeBase == 0 ||
        this->_timeBase > this->_rttEntries[0].Timestamp().tv_sec)
      this->_timeBase = this->_rttEntries[0].Timestamp().tv_sec;
  return(this->_timeBase);
}

//----------------------------------------------------------------------------
//     uint32_t ArtsRttTimeSeriesTableData::TimeBase(uint32_t timeBase) 
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableData::TimeBase(uint32_t timeBase)
{
  this->_timeBase = timeBase;
  return(this->_timeBase);
}


//----------------------------------------------------------------------------
//                  vector<ArtsRttTimeSeriesTableEntry> &                  
//                  ArtsRttTimeSeriesTableData::RttEntries() const         
//............................................................................
//  
//----------------------------------------------------------------------------
vector<ArtsRttTimeSeriesTableEntry> &
ArtsRttTimeSeriesTableData::RttEntries() const
{
  return(this->_rttEntries);
}

//----------------------------------------------------------------------------
//          void ArtsRttTimeSeriesTableData::
//          AddRttEntry(const ArtsRttTimeSeriesTableEntry & rttEntry) 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsRttTimeSeriesTableData::
AddRttEntry(const ArtsRttTimeSeriesTableEntry & rttEntry)
{
  this->_rttEntries.push_back(rttEntry);
  if (this->_timeBase == 0 || this->_timeBase > rttEntry.Timestamp().tv_sec)
    this->_timeBase = rttEntry.Timestamp().tv_sec;
  
  return;
}

//----------------------------------------------------------------------------
//     istream & ArtsRttTimeSeriesTableData::read(istream& is,
//                                                uint8_t version)         
//............................................................................
//  
//----------------------------------------------------------------------------
istream & ArtsRttTimeSeriesTableData::read(istream& is, uint8_t version)
{
  uint32_t numRttEntries, rttEntryNum;
  ArtsRttTimeSeriesTableEntry  rttEntry;
  uint32_t                     rttUsecs;
  uint32_t                     timestampSecs, timestampUsecs;
  struct timeval               tv;

  if (! this->_rttEntries.empty())
    this->_rttEntries.clear();
  
  g_ArtsLibInternal_Primitive.ReadUint32(is,this->_timeBase,
                                         sizeof(this->_timeBase));
  g_ArtsLibInternal_Primitive.ReadUint32(is,numRttEntries,
                                         sizeof(numRttEntries));
  uint32_t  prevSecsOffset = 0;
  for (rttEntryNum = 0; rttEntryNum != numRttEntries; ++rttEntryNum) {
    rttEntry.read(is,this->_timeBase,prevSecsOffset,version);
    prevSecsOffset = rttEntry.Timestamp().tv_sec - this->_timeBase;
    this->_rttEntries.push_back(rttEntry);
  }
  return(is);
}

//----------------------------------------------------------------------------
//     int ArtsRttTimeSeriesTableData::read(int fd, uint8_t version) 
//............................................................................
//  
//----------------------------------------------------------------------------
int ArtsRttTimeSeriesTableData::read(int fd, uint8_t version)
{
  uint32_t numRttEntries, rttEntryNum;
  ArtsRttTimeSeriesTableEntry  rttEntry;
  uint32_t                     rttUsecs;
  uint32_t                     timestampSecs, timestampUsecs;
  struct timeval               tv;

  if (this->_rttEntries.size() > 0)
    this->_rttEntries.clear();
  
  int rc = g_ArtsLibInternal_Primitive.ReadUint32(fd,this->_timeBase,
                                                  sizeof(this->_timeBase));
  if (rc < sizeof(this->_timeBase))
    return(-1);
  int bytesRead = rc;
  
  rc = g_ArtsLibInternal_Primitive.ReadUint32(fd,numRttEntries,
                                              sizeof(numRttEntries));
  if (rc < sizeof(numRttEntries))
    return(-1);
  bytesRead += rc;
  
  uint32_t  prevSecsOffset = 0;
  for (rttEntryNum = 0; rttEntryNum < numRttEntries; ++rttEntryNum) {
    rc = rttEntry.read(fd,this->_timeBase,prevSecsOffset,version);
    if (rc < 0)
      return(-1);
    bytesRead += rc;
    prevSecsOffset = rttEntry.Timestamp().tv_sec - this->_timeBase;
    this->_rttEntries.push_back(rttEntry);
  }

  return(bytesRead);
}

//----------------------------------------------------------------------------
//   uint32_t ArtsRttTimeSeriesTableData::Length(uint8_t version) const 
//............................................................................
//  
//----------------------------------------------------------------------------
uint32_t ArtsRttTimeSeriesTableData::Length(uint8_t version) const
{
  uint32_t    length = 0;
  uint32_t    rttEntryNum, numRttEntries;
  uint32_t    timestampSecs;

  length += sizeof(this->_timeBase);
  length += sizeof(numRttEntries);   //  number of entries

  timestampSecs = this->_rttEntries[0].Timestamp().tv_sec;
  numRttEntries = this->_rttEntries.size();
  
  uint32_t prevSecsOffset = 0;
  for (rttEntryNum = 0; rttEntryNum != numRttEntries; ++rttEntryNum) {
    length += this->_rttEntries[rttEntryNum].Length(timestampSecs,
                                                    prevSecsOffset,version);
    prevSecsOffset = this->_rttEntries[rttEntryNum].Timestamp().tv_sec -
      timestampSecs;
  }
  return(length);
}

//----------------------------------------------------------------------------
//   ostream & ArtsRttTimeSeriesTableData::write(ostream & os, 
//                                               uint8_t version) const 
//............................................................................
//  
//----------------------------------------------------------------------------
ostream & ArtsRttTimeSeriesTableData::write(ostream & os,
                                            uint8_t version) const
{
  uint32_t             numRttEntries, rttEntryNum;
  uint32_t             rttUsecs;
  uint32_t             timestampSecs, timestampUsecs;
  uint32_t             timestampSecsOffset;
  uint8_t              prevTimestampSecsSize = 1;
  uint32_t             prevOffset = 0;
  
  if (this->_rttEntries.size() > 0) {
    if (this->_timeBase == 0 ||
        this->_timeBase > this->_rttEntries[0].Timestamp().tv_sec) {
      this->_timeBase = this->_rttEntries[0].Timestamp().tv_sec;
    }
  }
  g_ArtsLibInternal_Primitive.WriteUint32(os,this->_timeBase,
                                          sizeof(this->_timeBase));
  
  numRttEntries = this->_rttEntries.size();
  g_ArtsLibInternal_Primitive.WriteUint32(os,numRttEntries,
                                          sizeof(numRttEntries));

  for (rttEntryNum = 0; rttEntryNum != numRttEntries; ++rttEntryNum) {
    this->_rttEntries[rttEntryNum].write(os,this->_timeBase,prevOffset,
                                         version);
    prevOffset =
      this->_rttEntries[rttEntryNum].Timestamp().tv_sec - this->_timeBase;
  }
  return(os);
}

//----------------------------------------------------------------------------
//  int ArtsRttTimeSeriesTableData::write(int fd, uint8_t version) const 
//............................................................................
//  
//----------------------------------------------------------------------------
int ArtsRttTimeSeriesTableData::write(int fd, uint8_t version) const
{
  uint32_t             numRttEntries, rttEntryNum;
  uint32_t             rttUsecs;
  uint32_t             timestampSecs, timestampUsecs;
  uint32_t             timestampSecsOffset;
  uint8_t              prevTimestampSecsSize = 1;
  uint32_t             prevOffset = 0;
  
  if (this->_rttEntries.size() > 0) {
    if (this->_timeBase == 0 ||
        this->_timeBase > this->_rttEntries[0].Timestamp().tv_sec) {
      this->_timeBase = this->_rttEntries[0].Timestamp().tv_sec;
    }
  }
  int rc = g_ArtsLibInternal_Primitive.WriteUint32(fd,this->_timeBase,
                                                   sizeof(this->_timeBase));
  if (rc < sizeof(this->_timeBase))
    return(-1);
  int bytesWritten = rc;
  
  numRttEntries = this->_rttEntries.size();
  rc = g_ArtsLibInternal_Primitive.WriteUint32(fd,numRttEntries,
                                               sizeof(numRttEntries));
  if (rc < sizeof(numRttEntries))
    return(-1);
  bytesWritten += rc;
  
  for (rttEntryNum = 0; rttEntryNum != numRttEntries; ++rttEntryNum) {
    rc = this->_rttEntries[rttEntryNum].write(fd,this->_timeBase,prevOffset,
                                              version);
    if (rc < 0)
      return(-1);
    bytesWritten += rc;
    
    prevOffset =
      this->_rttEntries[rttEntryNum].Timestamp().tv_sec - this->_timeBase;
  }
  
  return(bytesWritten);
}

//----------------------------------------------------------------------------
//        void ArtsRttTimeSeriesTableData::SortEntriesByTimestamp() 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsRttTimeSeriesTableData::SortEntriesByTimestamp()
{
  sort(this->_rttEntries.begin(),this->_rttEntries.end(),
       ArtsRttTimeSeriesTableEntryTimestampsLess());
  if (this->_rttEntries.size() > 0)
    if (this->_timeBase == 0 ||
        this->_timeBase > this->_rttEntries[0].Timestamp().tv_sec)
      this->_timeBase = this->_rttEntries[0].Timestamp().tv_sec;
  
  return;
}

//----------------------------------------------------------------------------
//                   const ArtsRttTimeSeriesTableEntry & 
//                   ArtsRttTimeSeriesTableData::RttMax() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttMax() const
{
  vector<ArtsRttTimeSeriesTableEntry>                  rttVect;
  vector<ArtsRttTimeSeriesTableEntry>::const_iterator  rttIter;
  static ArtsRttTimeSeriesTableEntry                   rttEntry;
  
  for (rttIter = this->_rttEntries.begin();
       rttIter != this->_rttEntries.end(); ++rttIter) {
    if ((*rttIter).Rtt() != ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      rttVect.push_back(*rttIter);
    }
  }
  if (rttVect.size() > 0) {
    rttEntry = *(max_element(rttVect.begin(),rttVect.end(),
                             ArtsRttTimeSeriesTableEntryLessRtt()));
    return(rttEntry);
  }
  else {
    struct timeval timestamp;
    timestamp.tv_sec = 0;
    timestamp.tv_usec = 0;
    rttEntry.Rtt(0);
    rttEntry.Timestamp(timestamp);
    return(rttEntry);
  }
}

//----------------------------------------------------------------------------
//                   const ArtsRttTimeSeriesTableEntry & 
//                   ArtsRttTimeSeriesTableData::RttMin() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttMin() const
{
  vector<ArtsRttTimeSeriesTableEntry>                  rttVect;
  vector<ArtsRttTimeSeriesTableEntry>::const_iterator  rttIter;
  static ArtsRttTimeSeriesTableEntry                   rttEntry;
  
  for (rttIter = this->_rttEntries.begin();
       rttIter != this->_rttEntries.end(); ++rttIter) {
    if ((*rttIter).Rtt() != ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      rttVect.push_back(*rttIter);
    }
  }
  if (rttVect.size() > 0) {
    rttEntry = *(min_element(rttVect.begin(),rttVect.end(),
                             ArtsRttTimeSeriesTableEntryLessRtt()));
    return(rttEntry);
  }
  else {
    struct timeval timestamp;
    timestamp.tv_sec = 0;
    timestamp.tv_usec = 0;
    rttEntry.Rtt(0);
    rttEntry.Timestamp(timestamp);
    return(rttEntry);
  }
}

//----------------------------------------------------------------------------
//               const ArtsRttTimeSeriesTableEntry & 
//               ArtsRttTimeSeriesTableData::RttPercentile() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttPercentile(int percentile) const
{
  int  percentilePosition;
  vector<ArtsRttTimeSeriesTableEntry> rttVect;
  vector<ArtsRttTimeSeriesTableEntry>::const_iterator rttIter;
  static ArtsRttTimeSeriesTableEntry rttEntry;
  
  assert((percentile >= 0) && (percentile <= 100));
  
  for (rttIter = this->_rttEntries.begin();
       rttIter != this->_rttEntries.end(); ++rttIter) {
    if ((*rttIter).Rtt() != ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      rttVect.push_back(*rttIter);
    }
  }
  
  if (rttVect.size() > 0) {
    percentilePosition = (int)((rttVect.size() - 1) * (percentile/100.0));
    nth_element(rttVect.begin(),rttVect.begin() + percentilePosition,
                rttVect.end(),ArtsRttTimeSeriesTableEntryLessRtt());
    rttEntry = rttVect[percentilePosition];
    return(rttEntry);
  }
  else {
    struct timeval timestamp;
    timestamp.tv_sec = 0;
    timestamp.tv_usec = 0;
    rttEntry.Rtt(0);
    rttEntry.Timestamp(timestamp);
    return(rttEntry);
  }
}

//----------------------------------------------------------------------------
//     size_t ArtsRttTimeSeriesTableData:: 
//     RttPercentiles(const vector<int> & percentiles, 
//                    vector<uint32_t> & results) const 
//............................................................................
//  
//----------------------------------------------------------------------------
size_t ArtsRttTimeSeriesTableData::
RttPercentiles(const vector<int> & percentiles,
               vector<uint32_t> & results) const
{
  vector<ArtsRttTimeSeriesTableEntry> rttVect(this->_rttEntries);

  if (! results.empty())
    results.clear();
  
  if (! rttVect.empty()) {
    int                          percentilePosition;
    vector<int>::const_iterator  pctIter;
    for (pctIter = percentiles.begin();
         pctIter != percentiles.end(); ++pctIter) {
      assert((*pctIter >= 0) && (*pctIter <= 100));
      percentilePosition = (int)((rttVect.size() - 1) * (*pctIter/100.0));
      nth_element(rttVect.begin(),rttVect.begin() + percentilePosition,
                  rttVect.end(),ArtsRttTimeSeriesTableEntryLessRtt());
      results.push_back(rttVect[percentilePosition].Rtt());
    }
  }

  return(results.size());
}

//----------------------------------------------------------------------------
//                   const ArtsRttTimeSeriesTableEntry & 
//                   ArtsRttTimeSeriesTableData::RttMedian() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttMedian() const
{
  return(this->RttPercentile(50));
}

//----------------------------------------------------------------------------
//                   const ArtsRttTimeSeriesTableEntry & 
//                   ArtsRttTimeSeriesTableData::RttLowerQuartile() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttLowerQuartile() const
{
  return(this->RttPercentile(25));
}

//----------------------------------------------------------------------------
//                   const ArtsRttTimeSeriesTableEntry & 
//                   ArtsRttTimeSeriesTableData::RttUpperQuartile() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const ArtsRttTimeSeriesTableEntry &
ArtsRttTimeSeriesTableData::RttUpperQuartile() const
{
  return(this->RttPercentile(75));
}

//----------------------------------------------------------------------------
//       double ArtsRttTimeSeriesTableData::AveragePacketLoss() const 
//............................................................................
//  
//----------------------------------------------------------------------------
double ArtsRttTimeSeriesTableData::AveragePacketLoss() const
{
  if (this->_rttEntries.size() > 0) {
    uint32_t  droppedPackets = 0;
    vector<ArtsRttTimeSeriesTableEntry>::const_iterator rttIter;
    
    for (rttIter = this->_rttEntries.begin();
         rttIter != this->_rttEntries.end(); ++rttIter) {
      if (rttIter->Rtt() == ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
        ++droppedPackets;
      }
    }

    double pktLoss = (droppedPackets * 100.0) / this->_rttEntries.size();
    return(pktLoss);
  }
  else {
    return(0.0);
  }
}

//----------------------------------------------------------------------------
//             void ArtsRttTimeSeriesTableData::ClearRttData() 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsRttTimeSeriesTableData::ClearRttData()
{
  if (this->_rttEntries.size() > 0) {
    this->_rttEntries.clear();
  }
  this->_timeBase = 0;
  return;
}

  
//----------------------------------------------------------------------------
// ostream& operator << (ostream& os, 
//                     const ArtsRttTimeSeriesTableData & rttTimeSeriesTable) 
//............................................................................
//  
//----------------------------------------------------------------------------
ostream& operator << (ostream& os,
                      const ArtsRttTimeSeriesTableData & rttTimeSeriesTable)
{
  struct in_addr   addrIn;
  uint32_t         rttNum;
  struct tm       *Tm;
  time_t           tsSecs;
  double           tsSecsFraction;
  
  os << "RTT TIME SERIES OBJECT DATA" << endl;
  for (rttNum = 0; rttNum < rttTimeSeriesTable.RttEntries().size(); ++rttNum) {
    tsSecs = rttTimeSeriesTable.RttEntries()[rttNum].Timestamp().tv_sec;
    tsSecsFraction =
      rttTimeSeriesTable.RttEntries()[rttNum].Timestamp().tv_usec / 1000.0;
    
    Tm = localtime(&tsSecs);
    os << setiosflags(ios::internal);
    os << "\t    timestamp: " << 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
       << "." << setw(3) << (int)(tsSecsFraction)
       << " ("
       << hex << (int)(tsSecs) << ")" << dec;
    os << setfill(' ');
    os << resetiosflags(ios::internal);
    os << "   RTT: ";
    if (rttTimeSeriesTable.RttEntries()[rttNum].Rtt() ==
        ArtsRttTimeSeriesTableEntry::k_droppedPacketRtt) {
      os << "lost packet" << endl;
    }
    else {
      os << rttTimeSeriesTable.RttEntries()[rttNum].Rtt()/1000.0
         << " ms" << endl;
    }
  }
  return(os);
}

uint32_t ArtsRttTimeSeriesTableEntry::_numObjects = 0;
uint32_t ArtsRttTimeSeriesTableData::_numObjects = 0;
