/* aggregate.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 <iostream>
#include <libiclass/time>
#include <unistd.h>

#include "aggregate.h"

using namespace iclass;
using namespace trafficmeter;

using std::cerr;
using std::cout;
using std::endl;
using std::runtime_error;
using std::vector;
using std::literals::string_literals::operator""s;

/**************************************************************************************************************/
Aggregate::Aggregate( vector<Source*>& sources, const Param& param ) :
    sources( sources ),
    packetDirectory( PACKET_DIR, SUFFIX_HASH_LENGTH ),
    flowDirectory( FLOW_DIR, SUFFIX_HASH_LENGTH ),
    statDirectory( STAT_DIR, PREFIX_HASH_LENGTH ),
    flow2Directory( FLOW2_DIR, 0 )
{
    if ( !packetDirectory.isCreated() )
    {
        throw runtime_error( "\tdirectory error: cannot create ./"s + PACKET_DIR );
    }

    if ( !flowDirectory.isCreated() )
    {
        throw runtime_error( "\tdirectory error: cannot create ./"s + FLOW_DIR );
    }

    if ( !statDirectory.isCreated() )
    {
        throw runtime_error( "\tdirectory error: cannot create ./"s + STAT_DIR );
    }

    if ( !flow2Directory.isCreated() )
    {
        throw runtime_error( "\tdirectory error: cannot create ./"s + FLOW2_DIR );
    }

    flow2Writer = new Flow2Writer( &flow2Directory );
    nameWriter = new NameWriter;

    for ( Source* src : sources )
    {
        Scheduler*   sched = new Scheduler( src );

        schedulers.push_back( sched );

        for ( int threadNo = 0; threadNo < param.nExtractors; ++threadNo )
        {
            Extractor*  ext = new Extractor( src, param.nExtractorChunks );

            extractors.push_back( ext );
            sched->attach( ext );
            ext->attach( flow2Writer );
            ext->attach( nameWriter );

            if ( param.shouldSavePackets )
            {
                PacketWriter*   pw = new PacketWriter( &packetDirectory, param.nCollectionChunks );

                packetWriters.push_back( pw );
                ext->attach( pw );
            }
        }
    }

    for ( int threadNo = 0; threadNo < param.nStatWriters; ++threadNo )
    {
        StatWriter*     sw = new StatWriter( &statDirectory, threadNo, param.nStatWriters );

        statWriters.push_back( sw );
    }

    for ( int threadNo = 0; threadNo < param.nFlowWriters; ++threadNo )
    {
        FlowWriter*     fw = new FlowWriter( &flowDirectory, threadNo, param.nFlowWriters );

        flowWriters.push_back( fw );

        for ( Extractor* ext : extractors )
        {
            ext->attach( fw );
        }

        for ( StatWriter* sw : statWriters )
        {
            fw->attach( sw );
        }
    }
}

/**************************************************************************************************************/
Aggregate::~Aggregate()
{
    for ( Scheduler* sched : schedulers )
    {
        delete sched;
    }

    for ( Extractor* ext : extractors )
    {
        delete ext;
    }

    for ( PacketWriter* pw : packetWriters )
    {
        delete pw;
    }

    for ( FlowWriter* fw : flowWriters )
    {
        delete fw;
    }

    for ( StatWriter* sw : statWriters )
    {
        delete sw;
    }

    delete flow2Writer;
    delete nameWriter;
}

/**************************************************************************************************************/
void
Aggregate::start()
{
    nameWriter->start();
    usleep( 2000 );
    flow2Writer->start();
    usleep( 2000 );

    for ( StatWriter* sw : statWriters )
    {
        sw->start();
        usleep( 2000 );
    }

    for ( FlowWriter* fw : flowWriters )
    {
        fw->start();
        usleep( 2000 );
    }

    for ( PacketWriter* pw : packetWriters )
    {
        pw->start();
        usleep( 2000 );
    }

    for ( Extractor* ext : extractors )
    {
        ext->start();
        usleep( 2000 );
    }

    for ( Scheduler* sched : schedulers )
    {
        sched->start( priorityt::min(SCHED_RR) + 1 );
        usleep( 5000 );
    }
}

/**************************************************************************************************************/
void
Aggregate::stop()
{
    for ( Scheduler* sched : schedulers )
    {
        sched->stop();
    }

    for ( Extractor* ext : extractors )
    {
        ext->stop();
    }

    for ( PacketWriter* pw : packetWriters )
    {
        pw->stop();
    }

    for ( FlowWriter* fw : flowWriters )
    {
        fw->stop();
    }

    for ( StatWriter* sw : statWriters )
    {
        sw->stop();
    }

    flow2Writer->stop();
    nameWriter->stop();
}
