/* tmpacket.cc
****************************************************************************************************************
****************************************************************************************************************

    Copyright (C) 2015, 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 <cstdlib>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <libiclass/time>
#include <getopt.h>

#include "packetquery.h"
#include "path.h"
#include "resourceusage.h"
#include "tmdefs.h"

using namespace iclass;
using namespace trafficmeter;

using std::cerr;
using std::cout;
using std::endl;
using std::fixed;
using std::invalid_argument;
using std::runtime_error;
using std::setprecision;
using std::setw;
using std::string;

static const struct option  LONG_OPTIONS_[] =
{
    { "start",      required_argument, nullptr, 's' },
    { "end",        required_argument, nullptr, 'e' },
    { "vlan",       required_argument, nullptr, 'l' },
    { "packets",    no_argument,       nullptr, 'p' },
    { "version",    no_argument,       nullptr, 'v' },
    { "help",       no_argument,       nullptr, 'h' },
    { nullptr,      0,                 nullptr,  0  }
};

/**************************************************************************************************************/
void
printHeader( const PacketQuery::Stat& statistics )
{
    if ( statistics.shouldCountFlows() )
    {
        cout << "           i.flows   i.packets      i.traffic     o.flows   o.packets      o.traffic\n";
    }
    else
    {
        cout << "            i.traffic      o.traffic\n";
    }
}

/**************************************************************************************************************/
void
printFooter( const PacketQuery::Stat& statistics )
{
    SummaryIo   summary = statistics.getSummary();

    cout << "Total:";

    if ( statistics.shouldCountFlows() )
    {
        cout << setw(12) << summary.nFlowsIn() << setw(12) << summary.nPacketsIn();
    }

    cout << setw(15) << summary.nBytesIn();

    if ( statistics.shouldCountFlows() )
    {
        cout << setw(12) << summary.nFlowsOut() << setw(12) << summary.nPacketsOut();
    }

    cout << setw(15) << summary.nBytesOut() << "\n";
}

/**************************************************************************************************************/
int
main( int argc, char* argv[] )
{
    string              progName( Path(argv[0]).fileName() );
    string              output( "STDOUT" );
    PacketQuery         query;
    PacketQuery::Stat   statistics;
    int                 opt;

    while ( (opt = getopt_long(argc,argv,"s:e:l:pvh",LONG_OPTIONS_,nullptr)) != -1 )
    {
        string  dieMessage;

        switch ( opt )
        {
            case 's':

                query.setbegin( ltimet(optarg, &dieMessage) );

                if ( !dieMessage.empty() )
                {
                    cerr << progName << ": incorrect start of interval: " << dieMessage << "\n";
                    return EXIT_FAILURE;
                }

                break;

            case 'e':

                query.setend( ltimet(optarg, &dieMessage) );

                if ( !dieMessage.empty() )
                {
                    cerr << progName << ": incorrect end of interval: " << dieMessage << "\n";
                    return EXIT_FAILURE;
                }

                break;

            case 'l':

                if ( !query.setVlanFilter(atoi(optarg)) )
                {
                    cerr << progName << ": incorrect vlan: " << optarg << "\n";
                    return EXIT_FAILURE;
                }

                break;

            case 'p':

                statistics.countFlows();
                break;

            case 'v':

                cout <<
                    "Trafficmeter IP Packet version " << VERSION << " for " << SYSTEM_TYPE << "\n"
                    "For info, please visit http://www.trafficmeter.org\n";

                return EXIT_SUCCESS;

            case 'h':
            default:

                cout <<
                    progName << " is the utility to get IP packets\n"
                    "Usage: " << progName << " [options] IP [output]\n"
                    "\tIP\t\t\t\tIP address: 'ddd.ddd.ddd.ddd'\n"
                    "\toutput\t\t\t\tpcap file to append, default standard output\n"
                    "\t-s --start=<time>\t\tStart of interval in ISO-8601 format, default last records\n"
                    "\t-e --end=<time>\t\t\tEnd of interval in ISO-8601 format, default last records\n"
                    "\t-l --vlan=<vlan>\t\tSet vlan filter, '0' - untagged traffic\n"
                    "\t-p --packets\t\t\tShow packet and flow statistics\n"
                    "\t-v --version\t\t\tPrint version information\n"
                    "\t-h --help\t\t\tPrint help message\n";

                return EXIT_SUCCESS;
        }
    }

    argc -= optind;
    argv += optind;

    if ( argc < 1 || argc > 2 )
    {
        cerr << progName << ": invalid number of arguments\n";
        return EXIT_FAILURE;
    }

    if ( !query.arrangeInterval() )
    {
        cerr << progName << ": incorrect interval specified\n";
        return EXIT_FAILURE;
    }

    try
    {
        query.setIp( ipaddr4t(*argv++) );
        argc--;
    }
    catch ( invalid_argument )
    {
        cerr << progName << ": incorrect IP address\n";
        return EXIT_FAILURE;
    }

    if ( argc )
    {
        output = *argv;

        if ( output.rfind(".pcap") != output.size() - 5 )
        {
            output += ".pcap";
        }
    }

    try
    {
        PcapWriting    pcap( output );

        if ( !pcap.isConsole() )
        {
            cout <<
                "Interval\t[ " << query.begin().str() << "; " << query.end().str() << " )\n"
                "Traffic of\t" << query.getIp().str() << "\n" << endl;
        }

        ResourceUsage   usage;

        usage.startMeasuring();
        query.fetch( pcap, statistics );
        usage.stopMeasuring();

        if ( !pcap.isConsole() )
        {
            printHeader( statistics );
            printFooter( statistics );
            cout << "(" << fixed << setprecision(3) << usage.seconds() << " sec)\n";
        }
    }
    catch ( runtime_error& e )
    {
        cerr << progName << ": " << e.what() << "\n";
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
