/**
 *  @file test3.cpp
 *  @brief  文字列表示での文字列処理の時間計測.
 */

#include <stddef.h>
#include <cstddef>
#include <stdlib.h>

#ifdef MALLOC_CHK
#include "malloc_chk.h"
#endif

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sstream>
#include <streambuf>
#include <string>
#include <iomanip>

#ifdef _WIN32
#include <windows.h>
#endif

#include "StrzStream.h"

using namespace std;


// ===========================================================================

// 時間計測関係
#ifdef _WIN32
#include <windows.h>
typedef unsigned __int64    PerfCnt_tick_t;
PerfCnt_tick_t PerfCnt_getTick()    { PerfCnt_tick_t c; QueryPerformanceCounter((LARGE_INTEGER*)&c); return c; }
PerfCnt_tick_t PerfCnt_tickPerSec() { static PerfCnt_tick_t s = 0; if (!s) QueryPerformanceFrequency((LARGE_INTEGER*)&s); return s;  }
#elif defined LINUX
#include <sys/resource.h>
typedef  double PerfCnt_tick_t;
PerfCnt_tick_t  PerfCnt_getTick() { struct rusage t; getrusage(RUSAGE_SELF, &t); return t.ru_utime.tv_sec * 1000000.0 + t.ru_utime.tv_usec; }
#define         PerfCnt_tickPerSec()    1000000.0
#else
#include <time.h>
typedef clock_t                 PerfCnt_tick_t;
#define PerfCnt_getTick()       clock()
#define PerfCnt_tickPerSec()    CLOCKS_PER_SEC
#endif




// ===========================================================================

void dummy_puts(const char* msg);       // 空関数だが最適化させないため、別ファイル.
void dummy_puts_set_mode(int mode);


// sprintfを使った場合.
void test_sprintf(const char* fname, unsigned line, unsigned long long val) {
    char buf[1024];
    sprintf(buf, "%-24s(%10d) : test %#016llx\n", fname, line, val);
    dummy_puts(buf);
}


static CharAryStream<1024>  s_charAryStream;

// 固定バッファのみのstream
void test_chararystream(const char* fname, unsigned line, unsigned long long val) {
    s_charAryStream.clear_buf();
    s_charAryStream << setw(24) << left << fname << right
                    << '(' << setw(10) << line
                    << ") : test "
                    << setfill('0') << setw(16) << hex << showbase << val
                    << '\n';
    dummy_puts( s_charAryStream.c_str() );
}



static stringstream s_stringstream;

// stringstream を予め生成して使った場合.
void test_stringstream(const char* fname, unsigned line, unsigned long long val) {
    s_stringstream.str(std::string(""));
    s_stringstream  << setw(24) << left << fname << right
                    << '(' << setw(10) << line
                    << ") : test "
                    << setfill('0') << setw(16) << hex << showbase << val
                    << '\n';
    dummy_puts( s_stringstream.str().c_str() );
}



// ===========================================================================
// 計測&チェック

typedef void (*test_print_func_t)(const char* fname, unsigned line, unsigned long long val);


double test(test_print_func_t test_print, size_t size, unsigned flags, const char* ttl) {
    unsigned char   verboss     = flags & 1;
    if (verboss) {
        fprintf(stderr, "\t%s:start\n", ttl);
        dummy_puts_set_mode(verboss);
        //size = 1;
      #ifdef MALLOC_CHK
        my_malloc_count_init();
      #endif
    }
    // ダミー表示=文字列処理時間 の計測.
    PerfCnt_tick_t  start_time  = PerfCnt_getTick();                // 計測開始
    for (unsigned j = 0; j < size; ++j) {
        test_print("abcdefghijk.lmn", 123456, 0xFFFFFF11ffULL );
    }
    PerfCnt_tick_t elapsed    = PerfCnt_getTick() - start_time;     // 計測終了

    double e = elapsed * 1.0 / (PerfCnt_tickPerSec() * size);
    if (verboss) {
        fprintf(stderr, "\t%s:end (%f)\n", ttl, e * 1000000.0);
      #ifdef MALLOC_CHK
        my_malloc_count_disp();
      #endif
    }
    return e;   // ソート時間.
}



void test_all(unsigned size, unsigned v) {
    const char* nm[32]     = {0};
    double      elaped[32] = { 0 };
    fprintf(stderr, "\nダミーメッセージの文字列処理時間の計測\n" );
    if (v)
        fprintf(stderr, "出力回数 %9d (の平均)\n", size);

    // 各ソートのチェック.
    unsigned k     = 0;
    {
        nm[k] = "sprintf      " ; elaped[k] = test(test_sprintf      , size, v, nm[k]); ++k;
        nm[k] = "CharAryStream" ; elaped[k] = test(test_chararystream, size, v, nm[k]); ++k;
        nm[k] = "stringstream " ; elaped[k] = test(test_stringstream , size, v, nm[k]); ++k;
    }

    // 実行結果の出力
    printf("\n*** ダミーメッセージの%d回の平均の文字列処理時間(μ秒)\n", size);
    printf("\n");
    for (unsigned j = 0; j < 32; ++j) {
        if (nm[j] == 0)
            continue;
        printf(",%-21s ,", nm[j]);
        double e = elaped[j]*1000000.0;
        if (e > 0)
            printf("%12.3f,", e);
        else
            printf("%12s,", "---");
        printf("\n");
    }
}


int main(int argc, char* argv[]) {
    unsigned    size       = 1; //100000;
    unsigned    v          = 0;

    // オプションチェック
    for (int i = 1; i < argc; ++i) {
        if (strncmp(argv[i], "-c",2) == 0) {    // ループ回数.
            size            = strtol(argv[i]+2,0,0);
            if (size < 1)
                size = 1;
        } else if (strcmp(argv[i], "-v") == 0) {
            v = 1;
        }
    }

    test_all(size, v);

    return 0;
}