/**
 *  @file test1.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 <string>
#include <streambuf>
#include <sstream>
#include <strstream>

#ifdef _WIN32
#include <windows.h>
#ifdef USE_WTL
#include <atlbase.h>
#include <atlapp.h>
#include <atlmisc.h>
using namespace WTL;
#endif
#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* msg, const char* fname, unsigned line) {
    char buf[1024];
    sprintf(buf, "%s(%d) : test> %s", fname, line, msg);
    dummy_puts(buf);
}


// ポインタやmemcpy等でメッセージ生成.
void test_memcpy(const char* msg, const char* fname, unsigned line) {
    char     buf[1024];
    unsigned l = strlen(fname);
    char*    p = (char*)memcpy(buf, fname, l);
    p += l;
    *p++ = '(';
    ltoa(line, p, 10);
    p += strlen(p);
    memcpy(p, ") : test> ", 10);
    p += 10;
    memcpy(p, msg, strlen(msg)+1);
    dummy_puts(buf);
}


// strcpy,strcatで楽にメッセージ生成.
void test_strcat(const char* msg, const char* fname, unsigned line) {
    char    lineBuf[32];
    ltoa(line, lineBuf, 10);

    char    buf[1024];
    strcpy(buf, fname);
    strcat(buf, "(");
    strcat(buf, lineBuf);
    strcat(buf, ") : test> ");
    strcat(buf, msg);
    dummy_puts(buf);
}


#if 1

// std::string で楽に書いた場合.
void test_std_string(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    dummy_puts((std::string(fname) + '(' + lineBuf + ") : test> " + msg).c_str() );
}



// std::string でなるべくアロケートが発生しないように書いた場合.
void test_std_string_rsv(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    std::string str;
    str.reserve(1024);
    str += fname;
    str += '(';
    str += lineBuf;
    str += ") : test> ";
    str += msg;
    dummy_puts( str.c_str() );
}


static std::string s_str;
void init_test_std_string_static() {
    s_str.reserve(1024);
}

// std::string で予め変数(メモリ)確保して、表示時のアロケートをたぶん無くした場合.
void test_std_string_static(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    std::string& rStr = s_str;
    rStr.clear();
    rStr += fname;
    rStr += '(';
    rStr += lineBuf;
    rStr += ") : test> ";
    rStr += msg;
    dummy_puts( rStr.c_str() );
}
#endif


#ifdef USE_WTL
// WTL::CString で、楽に書いた場合.
void test_ms_cstring(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    dummy_puts( CString(fname) + '(' + lineBuf + ") : test> " + msg );
}


// WTL::CString で、メモリ確保して順に足した場合.
void test_ms_cstring_rsv(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    CString     str;
    str.GetBuffer(1024);
    str += fname;
    str += '(';
    str += lineBuf;
    str += ") : test> ";
    str += msg;
    dummy_puts( str );
}


/*  // CStringのメモリ管理的に、メリットがなかったので破棄.
static CString s_cstring;
void init_test_ms_cstring_static() {s_cstring.GetBuffer(1024);}
void test_ms_cstring_static(const char* msg, const char* fname, unsigned line) {
    char lineBuf[32];
    ltoa(line, lineBuf, 10);
    CString& rStr = s_cstring;
    // rStr.Empty();
    rStr += fname;
    rStr += '(';
    rStr += lineBuf;
    rStr += ") : test> ";
    rStr += msg;
    dummy_puts( rStr );
}
*/


// WTL::CString のFormatで指定した場合.
void test_ms_cstring_format(const char* msg, const char* fname, unsigned line) {
    CString str;
    str.Format("%s(%d) : test> %s", fname, line, msg);
    dummy_puts( str );
}

#endif  // USE_WTL



// stringstream を使った場合.
void test_stringstream_local(const char* msg, const char* fname, unsigned line) {
    stringstream    ss;
    ss << fname << '(' << line << ") : test> " << msg;
    dummy_puts( ss.str().c_str() );
}


static stringstream s_stringstream;

// stringstream を予め生成して使った場合.
void test_stringstream_static(const char* msg, const char* fname, unsigned line) {
    s_stringstream.str(std::string(""));
    s_stringstream << fname << '(' << line << ") : test> " << msg;
    dummy_puts( s_stringstream.str().c_str() );
}


// strstream
void test_strstream_local(const char* msg, const char* fname, unsigned line) {
    char buf[1024];
    strstream   ss(buf, 1024, std::ios::out);
    ss << fname << '(' << line << ") : test> " << msg << ends;
    dummy_puts( buf );
}


static char         s_strstream_buf[1024];
static strstream    s_strstrem(s_strstream_buf, 1024, std::ios::out);


// strstream
void test_strstream_static(const char* msg, const char* fname, unsigned line) {
    s_strstrem.seekp(0, ios::beg);
    s_strstrem << fname << '(' << line << ") : test> " << msg << ends;
    dummy_puts( s_strstrem.str() );
}



// 固定バッファのみのstream
void test_chararystream_local(const char* msg, const char* fname, unsigned line) {
    CharAryStream<1024> ssb;
    ssb << fname << '(' << line << ") : test> " << msg;
    dummy_puts( ssb.c_str() );
}


static CharAryStream<1024>  s_charAryStream;

// 固定バッファのみのstream
void test_chararystream_static(const char* msg, const char* fname, unsigned line) {
    s_charAryStream.clear_buf();
    s_charAryStream << fname << '(' << line << ") : test> " << msg;
    dummy_puts( s_charAryStream.c_str() );
}



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

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


double test(test_print_func_t test_print, int m, 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
    }

    unsigned j;
    // ダミー表示=文字列処理時間 の計測.
    PerfCnt_tick_t  start_time  = PerfCnt_getTick();                // 計測開始
    switch (m) {
    case 0:
        for (j = 0; j < size; ++j)
            test_print("dummy message\n", "a.c", 1);
        break;
    case 1:
        for (j = 0; j < size; ++j)
            test_print("dummy message\n", "abcdefghijklmnopqrstuvwxyz0123456789.tmp", 1234567890);
        break;
    }
    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][3] = { {0} };

    init_test_std_string_static();

    fprintf(stderr, "\nダミーメッセージの文字列処理時間の計測\n" );
    if (v)
        fprintf(stderr, "出力回数 %9d (の平均)\n", size);

    // 各ソートのチェック.
    for (unsigned i = 0; i < 2; ++i) {
        unsigned k     = 0;
        nm[k] = "sprintf              " ; elaped[k][i] = test(test_sprintf              , i, size, v, nm[k]); ++k;
        nm[k] = "pointer+memcpy+strlen" ; elaped[k][i] = test(test_memcpy               , i, size, v, nm[k]); ++k;
        nm[k] = "strcpy+strcat        " ; elaped[k][i] = test(test_strcat               , i, size, v, nm[k]); ++k;
        nm[k] = "std::string (+)      " ; elaped[k][i] = test(test_std_string           , i, size, v, nm[k]); ++k;
        nm[k] = "std::string (rsv +=) " ; elaped[k][i] = test(test_std_string_rsv       , i, size, v, nm[k]); ++k;
        nm[k] = "std::string static   " ; elaped[k][i] = test(test_std_string_static    , i, size, v, nm[k]); ++k;
      #ifdef USE_WTL
        nm[k] = "WTL::CString (+)     " ; elaped[k][i] = test(test_ms_cstring           , i, size, v, nm[k]); ++k;
        nm[k] = "WTL::CString (rsv +=)" ; elaped[k][i] = test(test_ms_cstring_rsv       , i, size, v, nm[k]); ++k;
        nm[k] = "WTL::CString Format  " ; elaped[k][i] = test(test_ms_cstring_format    , i, size, v, nm[k]); ++k;
      #endif
        nm[k] = "stringstream local   " ; elaped[k][i] = test(test_stringstream_local   , i, size, v, nm[k]); ++k;
        nm[k] = "stringstream static  " ; elaped[k][i] = test(test_stringstream_static  , i, size, v, nm[k]); ++k;
        nm[k] = "strstream local      " ; elaped[k][i] = test(test_strstream_local      , i, size, v, nm[k]); ++k;
        nm[k] = "strstream static     " ; elaped[k][i] = test(test_strstream_static     , i, size, v, nm[k]); ++k;
        nm[k] = "CharAryStream local  " ; elaped[k][i] = test(test_chararystream_local  , i, size, v, nm[k]); ++k;
        nm[k] = "CharAryStream static " ; elaped[k][i] = test(test_chararystream_static , i, size, v, nm[k]); ++k;
    }

    // 実行結果の出力
    printf("\n*** ダミーメッセージの%d回の平均の文字列処理時間(μ秒)\n", size);
    printf("※数値→文字列の書式のない文字列操作ではltoaで数値を文字列化\n");
    printf("\n");
    for (unsigned j = 0; j < 32; ++j) {
        if (nm[j] == 0)
            continue;
        printf(",%-21s ", nm[j]);
        for (unsigned i = 0; i < 2; ++i) {
            double e = elaped[j][i]*1000000.0;
            if (e > 0)
                printf(",%12.3f", e);
            else
                printf(",%12s", "---");
        }
        printf("\n");
    }
}



int main(int argc, char* argv[]) {
    unsigned    size       = 1;
    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;
}