//===========================================================================
//  @(#) $Name: arts++-1-1-a12 $
//  @(#) $Id: ArtsPortMatrixAggregator.cc,v 1.2 2004/04/21 23:51:34 kkeys 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
//===========================================================================

#include <string>

#include "ArtsPortMatrixAggregator.hh"

using namespace std;

static const std::string rcsid = "@(#) $Name: arts++-1-1-a12 $ $Id: ArtsPortMatrixAggregator.cc,v 1.2 2004/04/21 23:51:34 kkeys Exp $";

//-------------------------------------------------------------------------
//         bool ArtsPortMatrixKeyValue::                      
//         operator < (const ArtsPortMatrixKeyValue keyValue) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool ArtsPortMatrixKeyValue::
operator < (const ArtsPortMatrixKeyValue keyValue) const
{
  if (this->srcPort < keyValue.srcPort)
    return(true);
  if (this->srcPort > keyValue.srcPort)
    return(false);
  
  if (this->dstPort < keyValue.dstPort)
    return(true);
  if (this->dstPort > keyValue.dstPort)
    return(false);

  return(false);
}

//---------------------------------------------------------------------------
//        bool ArtsPortMatrixKeyValue::
//        operator == (const ArtsPortMatrixKeyValue keyValue) const
//...........................................................................
//
//---------------------------------------------------------------------------
bool ArtsPortMatrixKeyValue::
operator == (const ArtsPortMatrixKeyValue keyValue) const
{
  if (this->srcPort == keyValue.srcPort &&
      this->dstPort == keyValue.dstPort)
    return(true);
  return(false);
}

//-------------------------------------------------------------------------
//   ArtsPortMatrixAggregator::ArtsPortMatrixAggregator(const Arts & arts)   
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsPortMatrixAggregator::ArtsPortMatrixAggregator(const Arts & arts)
{
  assert(arts.Header().Identifier() == artsC_OBJECT_PORT_MATRIX);
  
  this->_header = arts.Header();

  vector<ArtsAttribute>::const_iterator    inAttribute;

  for (inAttribute = arts.Attributes().begin();
       inAttribute != arts.Attributes().end(); ++inAttribute) {
    this->_attributes.push_back(*inAttribute);
  }

  counter_t               portMatrixCounter;
  ArtsPortMatrixKeyValue   key;
  vector<ArtsPortMatrixEntry>::const_iterator  inPortMatrixEntry;
  
  for (inPortMatrixEntry = arts.PortMatrixData()->PortEntries().begin();
       inPortMatrixEntry != arts.PortMatrixData()->PortEntries().end();
       ++inPortMatrixEntry) {
    key.srcPort = inPortMatrixEntry->Src();
    key.dstPort = inPortMatrixEntry->Dst();
    
    portMatrixCounter.Pkts = inPortMatrixEntry->Pkts();
    portMatrixCounter.Bytes = inPortMatrixEntry->Bytes();
    this->_portCounters[key] = portMatrixCounter;
  }
  this->_totalPkts = arts.PortMatrixData()->TotalPkts();
  this->_totalBytes = arts.PortMatrixData()->TotalBytes();
}

//-------------------------------------------------------------------------
//           void ArtsPortMatrixAggregator::Add(const Arts & arts)          
//.........................................................................
//  
//-------------------------------------------------------------------------
void ArtsPortMatrixAggregator::Add(const Arts & arts)
{
  assert(arts.Header().Identifier() == artsC_OBJECT_PORT_MATRIX);

  vector<ArtsAttribute>::iterator          myHostAttribute;

  for (myHostAttribute = this->_attributes.begin();
         myHostAttribute != this->_attributes.end();
         ++myHostAttribute) {
    if (myHostAttribute->Identifier() == artsC_ATTR_HOST)
      break;
  }
  
  vector<ArtsAttribute>::iterator          myPeriodAttribute;
  vector<ArtsAttribute>::const_iterator    inPeriodAttribute;
  
  for (myPeriodAttribute = this->_attributes.begin();
         myPeriodAttribute != this->_attributes.end();
         ++myPeriodAttribute) {
    if (myPeriodAttribute->Identifier() == artsC_ATTR_PERIOD)
      break;
  }
  
  inPeriodAttribute = arts.FindPeriodAttribute();

  const uint32_t *myPeriod = myPeriodAttribute->Period();
  const uint32_t *inPeriod = inPeriodAttribute->Period();
  
  if (inPeriod[0] < myPeriod[0]) {
    myPeriodAttribute->Period(inPeriod[0],myPeriod[1]);
  }
  if (inPeriod[1] > myPeriod[1]) {
    myPeriodAttribute->Period(myPeriod[0],inPeriod[1]);
  }
    
  counter_t               portCounter;
  ArtsPortMatrixKeyValue   key;
  vector<ArtsPortMatrixEntry>::const_iterator        inPortEntry;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::iterator   myPortEntry;

  for (inPortEntry = arts.PortMatrixData()->PortEntries().begin();
       inPortEntry != arts.PortMatrixData()->PortEntries().end();
       ++inPortEntry) {
    key.srcPort = inPortEntry->Src();
    key.dstPort = inPortEntry->Dst();
    
    myPortEntry = this->_portCounters.find(key);
    if (myPortEntry != this->_portCounters.end()) {
      (*myPortEntry).second.Pkts += inPortEntry->Pkts();
      (*myPortEntry).second.Bytes += inPortEntry->Bytes();
    }
    else {
      portCounter.Pkts = inPortEntry->Pkts();
      portCounter.Bytes = inPortEntry->Bytes();
      this->_portCounters[key] = portCounter;
    }
  }
  this->_totalPkts += arts.PortMatrixData()->TotalPkts();
  this->_totalBytes += arts.PortMatrixData()->TotalBytes();

  return;
}

//-------------------------------------------------------------------------
//  ArtsPortMatrix *ArtsPortMatrixAggregator::ConvertToArtsPortMatrix() const 
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsPortMatrix *ArtsPortMatrixAggregator::ConvertToArtsPortMatrix() const
{
  ArtsPortMatrixEntry  portEntry;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::const_iterator  portCounter;

  ArtsPortMatrix *artsPortMatrix = new ArtsPortMatrix();

  artsPortMatrix->Header() = this->_header;
  vector<ArtsAttribute>::const_iterator   inAttribute;

  for (inAttribute = this->_attributes.begin();
       inAttribute != this->_attributes.end(); ++inAttribute) {
    artsPortMatrix->Attributes().push_back(*inAttribute);
  }

  uint64_t  totalPkts = 0;
  uint64_t  totalBytes = 0;
  
  for (portCounter = this->_portCounters.begin();
       portCounter != this->_portCounters.end(); ++portCounter) {
    portEntry.Src((*portCounter).first.srcPort);
    portEntry.Dst((*portCounter).first.dstPort);
    portEntry.Pkts((*portCounter).second.Pkts);
    portEntry.Bytes((*portCounter).second.Bytes);
    artsPortMatrix->PortEntries().push_back(portEntry);
    totalPkts += portEntry.Pkts();
    totalBytes += portEntry.Bytes();
  }
  artsPortMatrix->TotalPkts(totalPkts);
  artsPortMatrix->TotalBytes(totalBytes);
  
  return(artsPortMatrix);
}

//-------------------------------------------------------------------------
//    uint16_t                                
//    ArtsPortMatrixAggregator::
//       PickPort(const ArtsPortMatrixKeyValue & portmKey, 
//                const ArtsPortChooser & portChooser) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t
ArtsPortMatrixAggregator::PickPort(const ArtsPortMatrixKeyValue & portmKey,
                                   const ArtsPortChooser & portChooser) const
{
  uint16_t  rc;
  
  if (portChooser.Matches(portmKey.srcPort)) {
    //  src port is in our selection set.
    if (portChooser.Matches(portmKey.dstPort)) {
      // dst port is also in our selection set.  Return the lower port.
      rc = (portmKey.srcPort < portmKey.dstPort) ?
             portmKey.srcPort : portmKey.dstPort;
      return(rc);
    }
    return(portmKey.srcPort);
  }
  if (portChooser.Matches(portmKey.dstPort)) {
    return(portmKey.dstPort);
  }
  return(0);
}

//-------------------------------------------------------------------------
//  ArtsSelectedPortTable *                         
//  ArtsPortMatrixAggregator::ConvertToArtsSelectedPortTable(const ArtsPortChooser & portChooser) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsSelectedPortTable *
ArtsPortMatrixAggregator::ConvertToArtsSelectedPortTable(const ArtsPortChooser & portChooser) const
{
  ArtsPortTableEntry  portEntry;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::const_iterator  portCounter;

  ArtsSelectedPortTable *selPortTable = new ArtsSelectedPortTable();

  selPortTable->Header() = this->_header;
  selPortTable->Header().Identifier(artsC_OBJECT_SELECTED_PORT);
  selPortTable->PortChooser() = portChooser;
  
  vector<ArtsAttribute>::const_iterator   inAttribute;
  for (inAttribute = this->_attributes.begin();
       inAttribute != this->_attributes.end(); ++inAttribute) {
    selPortTable->Attributes().push_back(*inAttribute);
  }

  map<uint16_t,ArtsPortTableEntry,less<uint16_t> >  portMap;
  
  for (portCounter = this->_portCounters.begin();
       portCounter != this->_portCounters.end(); ++portCounter) {
    uint16_t  selectedPort = this->PickPort((*portCounter).first,portChooser);
    portMap[selectedPort].PortNumber(selectedPort);
    if (selectedPort == (*portCounter).first.srcPort) {
      //  I picked the source port.
      portMap[selectedPort].OutPkts(portMap[selectedPort].OutPkts() +
                                    (*portCounter).second.Pkts);
      portMap[selectedPort].OutBytes(portMap[selectedPort].OutBytes() +
                                     (*portCounter).second.Bytes);
    }
    else {
      //  I picked the destination port.
      portMap[selectedPort].InPkts(portMap[selectedPort].InPkts() +
                                   (*portCounter).second.Pkts);
      portMap[selectedPort].InBytes(portMap[selectedPort].InBytes() +
                                    (*portCounter).second.Bytes);
    }
  }

  //  now I've got a map of ArtsPortTableEntry objects which I can
  //  place in the ArtsSelectedPortTable.
  map<uint16_t,ArtsPortTableEntry,less<uint16_t> >::const_iterator  tmpIter;
  
  for (tmpIter = portMap.begin(); tmpIter != portMap.end(); ++tmpIter) {
    selPortTable->PortEntries().push_back((*tmpIter).second);
    selPortTable->TotalPkts(selPortTable->TotalPkts() +
                            (*tmpIter).second.InPkts() +
                            (*tmpIter).second.OutPkts());
    selPortTable->TotalBytes(selPortTable->TotalBytes() +
                             (*tmpIter).second.InBytes() +
                             (*tmpIter).second.OutBytes());
  }
  selPortTable->SelectedPortTableData()->SortEntriesByBytes();
  
  return(selPortTable);
}

//----------------------------------------------------------------------------
//     ArtsSelectedPortTable * 
//     ArtsPortMatrixAggregator:: 
// ConvertToArtsSelectedPortTable(int numTopPorts, bool byPkts) const 
//----------------------------------------------------------------------------
ArtsSelectedPortTable *
ArtsPortMatrixAggregator::
ConvertToArtsSelectedPortTable(int numTopPorts, bool byPkts) const
{
  ArtsPortTableEntry  portEntry;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::const_iterator  portCounter;
  int                 portNum;
  ArtsPortTableEntry  portTableEntry;
  
  ArtsSelectedPortTable *selPortTable = new ArtsSelectedPortTable();

  selPortTable->Header() = this->_header;
  selPortTable->Header().Identifier(artsC_OBJECT_SELECTED_PORT);
  
  vector<ArtsAttribute>::const_iterator   inAttribute;
  for (inAttribute = this->_attributes.begin();
       inAttribute != this->_attributes.end(); ++inAttribute) {
    selPortTable->Attributes().push_back(*inAttribute);
  }
  
  vector<ArtsPortTableEntry> topPortsVector;
  for (portNum = 0; portNum < 65536; ++portNum) {
    portTableEntry.PortNumber(portNum);
    topPortsVector.push_back(portTableEntry);
  }
  
  for (portCounter = this->_portCounters.begin();
       portCounter != this->_portCounters.end(); ++portCounter) {
    topPortsVector[portCounter->first.srcPort].AddOutPkts(portCounter->second.Pkts);
    topPortsVector[portCounter->first.srcPort].AddOutBytes(portCounter->second.Bytes);
    topPortsVector[portCounter->first.dstPort].AddInPkts(portCounter->second.Pkts);
    topPortsVector[portCounter->first.dstPort].AddInBytes(portCounter->second.Bytes);
  }
  
  if (byPkts) {
    sort(topPortsVector.begin(),topPortsVector.end(),
         ArtsPortEntryGreaterPkts());
  }
  else {
    sort(topPortsVector.begin(),topPortsVector.end(),
         ArtsPortEntryGreaterBytes());
  }
  
  
  
  //  now I've got a vector of ArtsPortTableEntry objects which I can
  //  place in the ArtsSelectedPortTable.
  int  numPorts = 0;
  vector<ArtsPortTableEntry>::iterator  topPortVectIter;
  for (topPortVectIter = topPortsVector.begin();
       topPortVectIter != topPortsVector.end(); ++topPortVectIter) {
    if (numPorts >= numTopPorts) {
      break;
    }
    selPortTable->PortEntries().push_back(*topPortVectIter);
    selPortTable->TotalPkts(selPortTable->TotalPkts() +
                            topPortVectIter->InPkts() +
                            topPortVectIter->OutPkts());
    selPortTable->TotalBytes(selPortTable->TotalBytes() +
                             topPortVectIter->InBytes() +
                             topPortVectIter->OutBytes());
    selPortTable->PortChooser().AddPort(topPortVectIter->PortNumber());
    ++numPorts;
  }

  //  Do the 'other ports' entry (port 0)
  portTableEntry.PortNumber(0);
  portTableEntry.InPkts(0);
  portTableEntry.OutPkts(0);
  portTableEntry.InBytes(0);
  portTableEntry.OutBytes(0);
  for ( ; topPortVectIter != topPortsVector.end(); ++topPortVectIter) {
    portTableEntry.AddInPkts(topPortVectIter->InPkts());
    portTableEntry.AddOutPkts(topPortVectIter->OutPkts());
    portTableEntry.AddInBytes(topPortVectIter->InBytes());
    portTableEntry.AddOutBytes(topPortVectIter->OutBytes());
  }
  
  if ((portTableEntry.InPkts() > 0) ||
      (portTableEntry.OutPkts() > 0)) {
    bool  alreadyHavePortZero = false;
    for (topPortVectIter = selPortTable->PortEntries().begin();
         topPortVectIter != selPortTable->PortEntries().end();
         ++topPortVectIter) {
      if (topPortVectIter->PortNumber() == 0) {
        alreadyHavePortZero = true;
        topPortVectIter->AddInPkts(portTableEntry.InPkts());
        topPortVectIter->AddOutPkts(portTableEntry.OutPkts());
        topPortVectIter->AddInBytes(portTableEntry.InBytes());
        topPortVectIter->AddOutBytes(portTableEntry.OutBytes());
        break;
      }
    }
    if (! alreadyHavePortZero) {
      selPortTable->PortEntries().push_back(portTableEntry);
    }
    
    selPortTable->TotalPkts(selPortTable->TotalPkts() +
                            portTableEntry.InPkts() +
                            portTableEntry.OutPkts());
    selPortTable->TotalBytes(selPortTable->TotalBytes() +
                             portTableEntry.InBytes() +
                             portTableEntry.OutBytes());
  }

  selPortTable->SelectedPortTableData()->SortEntriesByBytes();
  
  return(selPortTable);
}


//-------------------------------------------------------------------------
//     vector<ArtsPortMatrixEntry> *                       
//     ArtsPortMatrixAggregator::TopSourcesByBytes(uint16_t numSources) 
//.........................................................................
//  
//-------------------------------------------------------------------------
vector<ArtsPortMatrixEntry> *
ArtsPortMatrixAggregator::TopSourcesByBytes(uint16_t numSources)
{
  map<uint16_t,counter_t,less<uint16_t> >  srcPortMap;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::iterator aggMapIter;
  
  //  load a map keyed by source PORT, with a counter_t as the value.
  map<uint16_t,counter_t,less<uint16_t> >::iterator  srcPortMapIter;
  for (aggMapIter = this->_portCounters.begin();
       aggMapIter != this->_portCounters.end(); ++aggMapIter) {
    srcPortMapIter = srcPortMap.find((*aggMapIter).first.srcPort);
    if (srcPortMapIter == srcPortMap.end()) {
      srcPortMap[(*aggMapIter).first.srcPort].Pkts = (*aggMapIter).second.Pkts;
      srcPortMap[(*aggMapIter).first.srcPort].Bytes = (*aggMapIter).second.Bytes;
    }
    else {
      (*srcPortMapIter).second.Pkts += (*aggMapIter).second.Pkts;
      (*srcPortMapIter).second.Bytes += (*aggMapIter).second.Bytes;
    }
  }

  //  Now I have a map<uint16_t,counter_t> with src PORT as key and
  //  counter_t as value.  Put it in a vector of ArtsPortMatrixEntry
  //  so I can sort it by byte count.
  ArtsPortMatrixEntry           srcPortEntry;
  vector<ArtsPortMatrixEntry>   srcPortVector;

  srcPortVector.reserve(srcPortMap.size());
  map<uint16_t,counter_t,less<uint16_t> >::iterator  srcMapIter;
  
  for (srcMapIter = srcPortMap.begin();
       srcMapIter != srcPortMap.end(); ++srcMapIter) {
    srcPortEntry.Src((*srcMapIter).first);
    srcPortEntry.Dst(0);
    srcPortEntry.Pkts((*srcMapIter).second.Pkts);
    srcPortEntry.Bytes((*srcMapIter).second.Bytes);
    srcPortVector.push_back(srcPortEntry);
  }

  // now sort the vector by the byte counters.
  sort(srcPortVector.begin(),srcPortVector.end(),
       ArtsPortMatrixEntryGreaterBytes());

  //  build the final vector we wish to return.
  vector<ArtsPortMatrixEntry>  *retVector;

  retVector = new vector<ArtsPortMatrixEntry>(srcPortVector.begin(),
                                              srcPortVector.begin() +
                                              numSources);
  return(retVector);
}


//-------------------------------------------------------------------------
//   vector<ArtsPortMatrixEntry> *                       
//   ArtsPortMatrixAggregator::TopDestinationsByBytes(uint16_t numSources) 
//.........................................................................
//  
//-------------------------------------------------------------------------
vector<ArtsPortMatrixEntry> *
ArtsPortMatrixAggregator::TopDestinationsByBytes(uint16_t numSources)
{
  map<uint16_t,counter_t,less<uint16_t> >  dstPortMap;
  map<ArtsPortMatrixKeyValue,counter_t,less<ArtsPortMatrixKeyValue> >::iterator aggMapIter;
  
  //  load a map keyed by source PORT, with a counter_t as the value.
  map<uint16_t,counter_t,less<uint16_t> >::iterator  dstPortMapIter;
  for (aggMapIter = this->_portCounters.begin();
       aggMapIter != this->_portCounters.end(); ++aggMapIter) {
    dstPortMapIter = dstPortMap.find((*aggMapIter).first.dstPort);
    if (dstPortMapIter == dstPortMap.end()) {
      dstPortMap[(*aggMapIter).first.dstPort].Pkts = (*aggMapIter).second.Pkts;
      dstPortMap[(*aggMapIter).first.dstPort].Bytes = (*aggMapIter).second.Bytes;
    }
    else {
      (*dstPortMapIter).second.Pkts += (*aggMapIter).second.Pkts;
      (*dstPortMapIter).second.Bytes += (*aggMapIter).second.Bytes;
    }
  }

  //  Now I have a map<uint16_t,counter_t> with dst PORT as key and
  //  counter_t as value.  Put it in a vector of ArtsPortMatrixEntry
  //  so I can sort it by byte count.
  ArtsPortMatrixEntry           dstPortEntry;
  vector<ArtsPortMatrixEntry>   dstPortVector;

  dstPortVector.reserve(dstPortMap.size());
  map<uint16_t,counter_t,less<uint16_t> >::iterator  dstMapIter;
  
  for (dstMapIter = dstPortMap.begin();
       dstMapIter != dstPortMap.end(); ++dstMapIter) {
    dstPortEntry.Dst((*dstMapIter).first);
    dstPortEntry.Src(0);
    dstPortEntry.Pkts((*dstMapIter).second.Pkts);
    dstPortEntry.Bytes((*dstMapIter).second.Bytes);
    dstPortVector.push_back(dstPortEntry);
  }

  // now sort the vector by the byte counters.
  sort(dstPortVector.begin(),dstPortVector.end(),
       ArtsPortMatrixEntryGreaterBytes());

  //  build the final vector we wish to return.
  vector<ArtsPortMatrixEntry>  *retVector;

  retVector = new vector<ArtsPortMatrixEntry>(dstPortVector.begin(),
                                              dstPortVector.begin() +
                                              numSources);
  return(retVector);
}

//-------------------------------------------------------------------------
//            uint64_t ArtsPortMatrixAggregator::TotalPkts() const           
//.........................................................................
//  
//-------------------------------------------------------------------------
uint64_t ArtsPortMatrixAggregator::TotalPkts() const
{
  return(this->_totalPkts);
}

//-------------------------------------------------------------------------
//           uint64_t ArtsPortMatrixAggregator::TotalBytes() const           
//.........................................................................
//  
//-------------------------------------------------------------------------
uint64_t ArtsPortMatrixAggregator::TotalBytes() const
{
  return(this->_totalBytes);
}
