/* statcollection.cc
****************************************************************************************************************
****************************************************************************************************************

    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2020, 2021 Askar
    Almatov

    This file is part of Trafficmeter.

    Trafficmeter is free software: you can redistribute it and/or modify it under the terms of the GNU General
    Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    Trafficmeter 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 Trafficmeter. If not, see
    <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <cassert>
#include <arpa/inet.h>

#include "statcollection.h"

using namespace iclass;
using namespace trafficmeter;

/**************************************************************************************************************/
StatCollection::StatCollection( int threadNo, int nThreads ) :
    threadNo_( threadNo ),
    nSlots_( ((1U<<PREFIX_HASH_LENGTH) - threadNo_ - 1)/nThreads + 1 ),
    pullSlots_c_( nullptr ),
    nextPullSlots_c_( nullptr )
{
    slots_c_ = new SlotVector( nSlots_ );
}

/**************************************************************************************************************/
StatCollection::~StatCollection()
{
    constexpr auto  hasRows = []( auto s ){ return !s.empty(); };

    this->lock();
    assert( pullSlots_c_ == nullptr );
    assert( nextPullSlots_c_ == nullptr );
    assert( find_if(slots_c_->begin(), slots_c_->end(), hasRows) == slots_c_->end() );
    delete slots_c_;
}

/**************************************************************************************************************/
void
StatCollection::sPush( int slotNo, const BeFlow& beFlow, const Summary& summary )
{
    this->lock();

    BeStat      beStat = { beFlow.sip, beFlow.vlan };
    SummaryIo   outgoing( 0, 0, 0, 1, summary.nPackets(), summary.nBytes() );
    auto&&      [ row, wasInserted ] = ( *slots_c_ )[ slotNo ].try_emplace( beStat, outgoing );

    if ( !wasInserted )
    {
        row->second += outgoing;
    }

    this->unlock();
}

/**************************************************************************************************************/
void
StatCollection::dPush( int slotNo, const BeFlow& beFlow, const Summary& summary )
{
    this->lock();

    BeStat      beStat = { beFlow.dip, beFlow.vlan };
    SummaryIo   incoming( 1, summary.nPackets(), summary.nBytes(), 0, 0, 0 );
    auto&&      [ row, wasInserted ] = ( *slots_c_ )[ slotNo ].try_emplace( beStat, incoming );

    if ( !wasInserted )
    {
        row->second += incoming;
    }

    this->unlock();
}

/**************************************************************************************************************/
void
StatCollection::finish( time_t group )
{
    this->lock();

    if ( pullSlots_c_ == nullptr )
    {
        group_c_ = group;
        pullSlots_c_ = slots_c_;
    }
    else
    {
        nextPullSlots_c_ = slots_c_;
    }

    slots_c_ = new SlotVector( nSlots_ );
    this->unlock();
}

/**************************************************************************************************************/
StatCollection::SlotVector*
StatCollection::pull()
{
    SlotVector*  slots;

    this->lock();
    slots = pullSlots_c_;

    if ( nextPullSlots_c_ == nullptr )
    {
        pullSlots_c_ = nullptr;
    }
    else
    {
        group_c_ += GROUP_FACTOR;
        pullSlots_c_ = nextPullSlots_c_;
        nextPullSlots_c_ = nullptr;
    }

    this->unlock();
    return slots;
}

/**************************************************************************************************************/
time_t
StatCollection::group() const
{
    this->lock();

    time_t  group = group_c_;

    this->unlock();
    return group;
}

/**************************************************************************************************************/
bool
StatCollection::isPullable() const
{
    this->lock();

    bool    isPullable = ( pullSlots_c_ != nullptr );

    this->unlock();
    return isPullable;
}

/**************************************************************************************************************/
long
StatCollection::nRows() const
{
    long    nRows = 0;

    this->lock();

    for ( const auto& s : *slots_c_ )
    {
        nRows += s.size();
    }

    if ( pullSlots_c_ != nullptr )
    {
        for ( const auto& s : *pullSlots_c_ )
        {
            nRows += s.size();
        }
    }

    if ( nextPullSlots_c_ != nullptr )
    {
        for ( const auto& s : *nextPullSlots_c_ )
        {
            nRows += s.size();
        }
    }

    this->unlock();
    return nRows;
}
