/* flow2collection.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 <cassert>

#include "flow2collection.h"
#include "tmdefs.h"

using namespace iclass;
using namespace trafficmeter;

using std::map;

/**************************************************************************************************************/
Flow2Collection::Flow2Collection() :
    hasActivity_c_( false )
{
    map_c_ = new map<BeFlow2,Summary2>;
    nextMap_c_ = new map<BeFlow2,Summary2>;
    oldestGroup_c_ = moduloRoundDown( time(nullptr), GROUP_FACTOR );
    newestGroup_c_ = oldestGroup_c_;
}

/**************************************************************************************************************/
Flow2Collection::~Flow2Collection()
{
    this->lock();
    assert( map_c_->empty() );
    assert( nextMap_c_->empty() );
    delete map_c_;
    delete nextMap_c_;
}

/**************************************************************************************************************/
void
Flow2Collection::push( const Metadata& meta )
{
    time_t      group = moduloRoundDown( meta.timestamp.sec(), GROUP_FACTOR );
    BeFlow2     beFlow2 = { meta.smac, meta.dmac, htons(meta.vlan), htons(meta.etype) };
    Summary2    summary( 1, meta.nBytes2, (meta.nBytes != 0), meta.nBytes );

    this->lock();
    hasActivity_c_ |= ( group <= oldestGroup_c_ );

    if ( group > newestGroup_c_ )
    {
        newestGroup_c_ = group;
    }

    auto&&  [ row, wasInserted ] = ( (group<=oldestGroup_c_)? map_c_ : nextMap_c_ )->try_emplace( beFlow2, summary );

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

    this->unlock();
}

/**************************************************************************************************************/
map<BeFlow2,Summary2>*
Flow2Collection::pull()
{
    map<BeFlow2,Summary2>*  pullMap;

    this->lock();
    pullMap = map_c_;
    map_c_ = nextMap_c_;
    nextMap_c_ = new map<BeFlow2,Summary2>;
    oldestGroup_c_ += GROUP_FACTOR;
    this->unlock();
    return pullMap;
}

/**************************************************************************************************************/
time_t
Flow2Collection::oldestGroup() const
{
    this->lock();

    time_t  oldestGroup = oldestGroup_c_;

    this->unlock();
    return oldestGroup;
}

/**************************************************************************************************************/
bool
Flow2Collection::isBusy() const
{
    this->lock();

    time_t  timeBound = oldestGroup_c_ + GROUP_FACTOR + 1;   // 1 second more to collect from unsorted queue
    bool    hasNextTime = newestGroup_c_ > oldestGroup_c_;
    bool    hasActivity = hasActivity_c_;

    hasActivity_c_ = false;
    this->unlock();
    return time( nullptr ) < timeBound || ( !hasNextTime && hasActivity );
}

/**************************************************************************************************************/
long
Flow2Collection::nRows() const
{
    this->lock();

    long  nRows = map_c_->size() + nextMap_c_->size();

    this->unlock();
    return nRows;
}
