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

#include "extractor.h"
#include "metadata.h"
#include "now.h"
#include "slotindex.h"

using namespace iclass;
using namespace trafficmeter;

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

constexpr const int     IDLE_DURATION_ = 50000;     // microseconds

/**************************************************************************************************************/
Extractor::Extractor( Source* source, int nChunks ) :
    source_( source ),
    inputQueue_( nChunks ),
    packetCollection_( nullptr ),
    flow2Collection_( nullptr ),
    nameCollection_( nullptr ),
    nProcessed_a_( 0 )
{
}

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

/**************************************************************************************************************/
void
Extractor::attach( PacketWriter* writer )
{
    assert( packetCollection_ == nullptr );
    packetCollection_ = writer->getCollection();
}

/**************************************************************************************************************/
void
Extractor::attach( FlowWriter* writer )
{
    flowCollections_.push_back( writer->createCollection() );
}

/**************************************************************************************************************/
void
Extractor::attach( Flow2Writer* writer )
{
    flow2Collection_ = writer->createCollection();
}

/**************************************************************************************************************/
void
Extractor::attach( NameWriter* writer )
{
    nameCollection_ = writer->createCollection();
}

/**************************************************************************************************************/
SubstrateQueue*
Extractor::inputQueue()
{
    return &inputQueue_;
}

/**************************************************************************************************************/
long
Extractor::queueSize() const
{
    return inputQueue_.size();
}

/**************************************************************************************************************/
uintmax_t
Extractor::nProcessed() const
{
    return nProcessed_a_;
}

/**************************************************************************************************************/
void
Extractor::routine()
{
    assert( !flowCollections_.empty() );
    assert( flow2Collection_ != nullptr );
    assert( nameCollection_ != nullptr );
    cout << Now() << "tid=" << tid() << " extractor started" << endl;

    while ( true )
    {
        usleep( IDLE_DURATION_ );

        Substrates*  substrates = inputQueue_.pull();

        if ( substrates == nullptr )
        {
            if ( stopped() )
            {
                break;
            }

            continue;
        }

        for ( Substrate* s : *substrates )
        {
            extract_( s );
        }

        delete substrates;
    }

    cout
        << Now() << "tid=" << tid() << " extractor finished "
        << "(" << queueSize() << "," << nProcessed() << ")" << endl;
}

/**************************************************************************************************************/
void
Extractor::extract_( Substrate* substrate )
{
    while ( !substrate->isEmpty() )
    {
        Packet&&    packet( substrate->getPacket(source_) );
        Metadata    meta( packet.timestamp(), packet.data2(), packet.nBytes2() );

        if ( meta.etype == 0x0800 ) // IPv4
        {
            if ( packetCollection_ != nullptr )
            {
                packetCollection_->push( meta, packet );
            }

            SlotIndex   index( meta, flowCollections_.size() );

            flowCollections_[ index.sThreadNo ]->push( index, meta );

            if ( index.dThreadNo != index.sThreadNo )
            {
                flowCollections_[ index.dThreadNo ]->push( index, meta );
            }
        }

        if ( meta.proto == 17 && meta.sport == 53 ) // UDP sport 53
        {
            nameCollection_->push( meta, packet );
        }

        flow2Collection_->push( meta );
        ++nProcessed_a_;
    }
}

