/* monitor.cc
****************************************************************************************************************
****************************************************************************************************************

    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2020, 2021, 2026 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 <iomanip>
#include <iostream>
#include <libiclass/time>
#include <unistd.h>

#include "aggregate.h"
#include "source.h"
#include "monitor.h"
#include "now.h"

using namespace iclass;
using namespace trafficmeter;

using std::cout;
using std::endl;
using std::setw;

constexpr const int     IDLE_DURATION_ = 50000;     // microseconds

/**************************************************************************************************************/
Monitor::Monitor( const Param& param ) :
    verbosity_( param.verbosity ),
    flowQuota_( param.flowQuota ),
    packetQuota_( param.packetQuota ),
    statQuota_( param.statQuota ),
    flow2Quota_( param.flow2Quota ),
    nReceivedPrevious_( 0 ),
    nDroppedPrevious_( 0 ),
    nPacketsWrittenPrevious_( 0 ),
    nFlowsWrittenPrevious_( 0 ),
    nStatsWrittenPrevious_( 0 ),
    nNamesWrittenPrevious_( 0 )
{
}

/**************************************************************************************************************/
Monitor::~Monitor()
{
}

/**************************************************************************************************************/
void
Monitor::bind( Aggregate* ag )
{
    aggregates_.push_back( ag );
    sweeper_.attach( &ag->flowDirectory );
    sweeper_.attach( &ag->packetDirectory );
    sweeper_.attach( &ag->statDirectory );
    sweeper_.attach( &ag->flow2Directory );
    ag->flowDirectory.setQuota( flowQuota_ );
    ag->packetDirectory.setQuota( packetQuota_ );
    ag->statDirectory.setQuota( statQuota_ );
    ag->flow2Directory.setQuota( flow2Quota_ );
}

/**************************************************************************************************************/
bool
Monitor::reconfigure( const Param& param )
{
    bool    changes = false;

    if ( param.verbosity != verbosity_ )
    {
        cout << "\tconfig verbosity: " << verbosity_ << " -> " << param.verbosity << endl;
        verbosity_ = param.verbosity;
        changes = true;
    }

    if ( param.flowQuota != flowQuota_ )
    {
        cout << "\tconfig flowquota: " << flowQuota_ << " -> " << param.flowQuota << endl;
        flowQuota_ = param.flowQuota;
        changes = true;

        for ( Aggregate* ag : aggregates_ )
        {
            ag->flowDirectory.setQuota( flowQuota_ );
        }
    }

    if ( param.packetQuota != packetQuota_ )
    {
        cout << "\tconfig packetquota: " << packetQuota_ << " -> " << param.packetQuota << endl;
        packetQuota_ = param.packetQuota;
        changes = true;

        for ( Aggregate* ag : aggregates_ )
        {
            ag->packetDirectory.setQuota( packetQuota_ );
        }
    }

    if ( param.statQuota != statQuota_ )
    {
        cout << "\tconfig statquota: " << statQuota_ << " -> " << param.statQuota << endl;
        statQuota_ = param.statQuota;
        changes = true;

        for ( Aggregate* ag : aggregates_ )
        {
            ag->statDirectory.setQuota( statQuota_ );
        }
    }

    if ( param.flow2Quota != flow2Quota_ )
    {
        cout << "\tconfig flow2quota: " << flow2Quota_ << " -> " << param.flow2Quota << endl;
        flow2Quota_ = param.flow2Quota;
        changes = true;

        for ( Aggregate* ag : aggregates_ )
        {
            ag->flow2Directory.setQuota( flow2Quota_ );
        }
    }

    return changes;
}


/**************************************************************************************************************/
void
Monitor::routine()
{
    cout << Now() << "tid=" << tid() << " monitor started" << endl;

    sweeper_.start();
    usleep( 500 );

    time_t  proceed = time( nullptr );

    while ( !stopped() )
    {
        if ( proceed > time(nullptr) )
        {
            usleep( IDLE_DURATION_ );
            continue;
        }

        checkDropped_();                // every second

        if ( proceed % 10 == 0 )
        {
            printSweep_();              // every 10 seconds
        }

        if ( proceed % 60 == 0 )
        {
            printState_();              // at 0th second of every minute
        }

        proceed++;
    }

    sweeper_.stop();
    printSweep_();
    printState_();

    cout <<
        "\tsession packets " << setw(15) << nReceivedPrevious_ << endl <<
        "\tsession flows   " << setw(15) << nFlowsWrittenPrevious_ << endl <<
        "\tsession stats   " << setw(15) << nStatsWrittenPrevious_ << endl <<
        "\tsession names   " << setw(15) << nNamesWrittenPrevious_ << endl <<
        Now() << "tid=" << tid() << " monitor finished" << endl;
}

/**************************************************************************************************************/
void
Monitor::checkDropped_()
{
    uintmax_t   nDropped = 0;

    for ( Aggregate* ag : aggregates_ )
    {
        for ( Source* src : ag->sources )
        {
            nDropped += src->nDropped();
        }
    }

    if ( nDropped > nDroppedPrevious_ )
    {
        cout << Now() << "dropped " << nDropped - nDroppedPrevious_ << ";" << endl;
        nDroppedPrevious_ = nDropped;
    }
}

/**************************************************************************************************************/
void
Monitor::printState_()
{
    uintmax_t   nReceived = 0;
    long        extQueue = 0;
    long        bucketQueue = 0;
    long        nFlows = 0;
    long        nNames = 0;
    uintmax_t   nPacketsWritten = 0;
    uintmax_t   nFlowsWritten = 0;
    uintmax_t   nStatsWritten = 0;
    uintmax_t   nNamesWritten = 0;

    for ( Aggregate* ag : aggregates_ )
    {
        for ( Source* src : ag->sources )
        {
            nReceived += src->nReceived();
        }

        for ( Extractor* ext : ag->extractors )
        {
            extQueue += ext->queueSize();
        }

        for ( PacketWriter* pw : ag->packetWriters )
        {
            bucketQueue += pw->queueSize();
            nPacketsWritten += pw->nWritten();
        }

        for ( FlowWriter* fw : ag->flowWriters )
        {
            nFlows += fw->nRows();
            nFlowsWritten += fw->nWritten();
        }

        for ( StatWriter* sw : ag->statWriters )
        {
            nStatsWritten += sw->nWritten();
        }

        nNames += ag->nameWriter->nRows();
        nNamesWritten += ag->nameWriter->nWritten();
    }

    nReceived -= nReceivedPrevious_;
    nPacketsWritten -= nPacketsWrittenPrevious_;
    nFlowsWritten -= nFlowsWrittenPrevious_;
    nStatsWritten -= nStatsWrittenPrevious_;
    nNamesWritten -= nNamesWrittenPrevious_;

    cout <<
        Now() << "received " << nReceived << "; eq " << extQueue << "; "
        "packet (" << bucketQueue << "," << nPacketsWritten << "); "
        "flow (" << nFlows << "," << nFlowsWritten << "," << nStatsWritten << "); "
        "name (" << nNames << "," << nNamesWritten << ");" << endl;

    nReceivedPrevious_ += nReceived;
    nPacketsWrittenPrevious_ += nPacketsWritten;
    nFlowsWrittenPrevious_ += nFlowsWritten;
    nStatsWrittenPrevious_ += nStatsWritten;
    nNamesWrittenPrevious_ += nNamesWritten;
}

/**************************************************************************************************************/
void
Monitor::printSweep_()
{
    auto&&  fileNames( sweeper_.removedFiles() );

    if ( !fileNames.empty() )
    {
        if ( verbosity_ > 1 )
        {
            for ( const auto& fn : fileNames )
            {
                cout << "\tquota exceeded: " << fn << " removed" << endl;
            }
        }
        else if ( verbosity_ == 1 )
        {
            cout << "\tquota exceeded: " << fileNames.size() << " files removed" << endl;
        }
    }
}

