/*
    Copyright 2005-2014 Intel Corporation.  All Rights Reserved.

    This file is part of Threading Building Blocks. Threading Building Blocks is free software;
    you can redistribute it and/or modify it under the terms of the GNU General Public License
    version 2  as  published  by  the  Free Software Foundation.  Threading Building Blocks 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 Threading Building Blocks; if not, write to the
    Free Software Foundation, Inc.,  51 Franklin St,  Fifth Floor,  Boston,  MA 02110-1301 USA

    As a special exception,  you may use this file  as part of a free software library without
    restriction.  Specifically,  if other files instantiate templates  or use macros or inline
    functions from this file, or you compile this file and link it with other files to produce
    an executable,  this file does not by itself cause the resulting executable to be covered
    by the GNU General Public License. This exception does not however invalidate any other
    reasons why the executable file might be covered by the GNU General Public License.
*/

// Internal Intel tool

#ifndef __STATISTICS_H__
#define __STATISTICS_H__

#define _CRT_SECURE_NO_DEPRECATE 1

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <time.h>

using namespace std;
typedef double value_t;

/*
   Statistical collector class.

   Resulting table output:
        +---------------------------------------------------------------------------+
        | [Date] <Title>...                                                         |
        +----------+----v----+--v---+----------------+------------+-..-+------------+
        | TestName | Threads | Mode | Rounds results | Stat_type1 | .. | Stat_typeN |
        +----------+---------+------+-+-+-+-..-+-+-+-+------------+-..-+------------+
        |          |         |      | | | | .. | | | |            |    |            |
        ..        ...       ...     ..................            ......           ..
        |          |         |      | | | | .. | | | |            |    |            |
        +----------+---------+------+-+-+-+-..-+-+-+-+------------+-..-+------------+

   Iterating table output:
        +---------------------------------------------------------------------------+
        | [Date] <TestName>, Threads: <N>, Mode: <M>; for <Title>...                |
        +----------+----v----+--v---+----------------+------------+-..-+------------+

*/

class StatisticsCollector
{
public:
    typedef map<string, string> Analysis_t;
    typedef vector<value_t> Results_t;

protected:
    StatisticsCollector(const StatisticsCollector &);

    struct StatisticResults
    {
        string              Name;
        string              Mode;
        int                 Threads;
        Results_t           Results;
        Analysis_t          Analysis;
    };

    // internal members
    //bool OpenFile;
    StatisticResults *CurrentKey;
    string Title;
    const char /**Name,*/ *ResultsFmt;
    string Name;
    //! Data
    typedef map<string, StatisticResults*> Statistics_t;
    Statistics_t Statistics;
    typedef vector<string> RoundTitles_t;
    RoundTitles_t RoundTitles;
    //TODO: merge those into one structure
    typedef map<string, string> Formulas_t;
    Formulas_t   Formulas;
    typedef set<string> AnalysisTitles_t;
    AnalysisTitles_t AnalysisTitles;
    typedef vector<pair<string, string> > RunInfo_t;
    RunInfo_t RunInfo;

public:
    struct TestCase {
        StatisticResults *access;
        TestCase() : access(0) {}
        TestCase(StatisticResults *link) : access(link) {}
        const char *getName() const { return access->Name.c_str(); }
        const char *getMode() const { return access->Mode.c_str(); }
        int getThreads()       const { return access->Threads; }
        const Results_t &getResults() const { return access->Results; }
        const Analysis_t &getAnalysis() const { return access->Analysis; }
    };

    enum Sorting {
        ByThreads, ByAlg
    };

    //! Data and output types
    enum DataOutput {
        // Verbosity level enumeration
        Statistic = 1,     //< Analytical data - computed after all iterations and rounds passed
        Result    = 2,     //< Testing data    - collected after all iterations passed
        Iteration = 3,     //< Verbose data    - collected at each iteration (for each size - in case of containers)
        // ExtraVerbose is not applicabe yet :) be happy, but flexibility is always welcome

        // Next constants are bit-fields
        Stdout   = 1<<8,    //< Output to the console
        TextFile = 1<<9,    //< Output to plain text file "name.txt" (delimiter is TAB by default)
        ExcelXML = 1<<10,   //< Output to Excel-readable XML-file "name.xml"
        HTMLFile = 1<<11,   //< Output to HTML file "name.html"
        PivotMode= 1<<15    //< Puts all the rounds into one columt to better fit for pivot table in Excel
    };

    //! Constructor. Specify tests set name which used as name of output files
    StatisticsCollector(const char *name, Sorting mode = ByThreads, const char *fmt = "%g")
        :  CurrentKey(NULL), ResultsFmt(fmt), Name(name), SortMode(mode) {}

    ~StatisticsCollector();

    //! Set tests set title, supporting printf-like arguments
    void SetTitle(const char *fmt, ...);

    //! Specify next test key
    TestCase SetTestCase(const char *name, const char *mode, int threads);
    //! Specify next test key
    void SetTestCase(const TestCase &t) { SetTestCase(t.getName(), t.getMode(), t.getThreads()); }
    //! Reserve specified number of rounds. Use for effeciency. Used mostly internally
    void ReserveRounds(size_t index);
    //! Add result of the measure
    void AddRoundResult(const TestCase &, value_t v);
    //! Add result of the current measure
    void AddRoundResult(value_t v) { if(CurrentKey) AddRoundResult(TestCase(CurrentKey), v); }
    //! Add title of round
    void SetRoundTitle(size_t index, const char *fmt, ...);
    //! Add numbered title of round
    void SetRoundTitle(size_t index, int num) { SetRoundTitle(index, "%d", num); }
    //! Get number of rounds
    size_t GetRoundsCount() const { return RoundTitles.size(); }
    // Set statistic value for the test
    void AddStatisticValue(const TestCase &, const char *type, const char *fmt, ...);
    // Set statistic value for the current test
    void AddStatisticValue(const char *type, const char *fmt, ...);
    //! Add Excel-processing formulas. @arg formula can contain more than one instances of
    //! ROUNDS template which transforms into the range of cells with result values
    //TODO://! #1 .. #n templates represent data cells from the first to the last
    //TODO: merge with Analisis
    void SetStatisticFormula(const char *name, const char *formula);
    //! Add information about run or compile parameters
    void SetRunInfo(const char *title, const char *fmt, ...);
    void SetRunInfo(const char *title, int num) { SetRunInfo(title, "%d", num); }

    //! Data output
    void Print(int dataOutput, const char *ModeName = "Mode");

private:
    Sorting SortMode;
};

//! using: Func(const char *fmt, ...) { vargf2buff(buff, 128, fmt);...
#define vargf2buff(name, size, fmt) \
    char name[size]; memset(name, 0, size); \
    va_list args; va_start(args, fmt); \
    vsnprintf(name, size-1, fmt, args); \
    va_end(args);


inline std::string Format(const char *fmt, ...) {
    vargf2buff(buf, 1024, fmt); // from statistics.h
    return std::string(buf);
}

#ifdef STATISTICS_INLINE
#include "statistics.cpp"
#endif
#endif //__STATISTICS_H__
