#include "speedTestClass.h"

//--------------------------------------------------------
//----------          speedTestClass          ------------
//--------------------------------------------------------
// -------------------------------------------------------

// -------------------------------------------------------
//! \brief  テーブル処理用変数.
//! \param  std::string 計測するときの名前.
//! \return なし.
void speedTestClass::st(std::string s)
{
    (this->*Switch)(s);             //!< 関数呼び出し
}

// -------------------------------------------------------
//! \brief  テーブル処理用変数.
//! \param  なし.
//! \return なし.
void speedTestClass::st()
{
    (this->*Switch)("");
}

// -------------------------------------------------------
//! \brief  カウンタをスタート.
//! \param  なし.
//! \return なし.
void speedTestClass::start(std::string memo)
{
    stringlist.push_back(memo);
    Switch = &speedTestClass::stop;        //!< stSwitch
#ifdef _WIN32
    QueryPerformanceCounter(&t_start);
#else
    gettimeofday(&t_start.tv,NULL);
//    __asm__ volatile ("rdtsc" : "=a" (t_start.LowPart), "=d" (t_start.HighPart));
#endif //_WIN32
}

// -------------------------------------------------------
//! \brief  カウンタをストップ.
//! \param  なし.
//! \return なし.
void speedTestClass::stop(std::string memo)
{
#ifdef _WIN32
    QueryPerformanceCounter(&t_end);    //!< パフォーマンスカウンタを呼び出す
	wlonglist.push_back(t_end.QuadPart - t_start.QuadPart);
#else
    gettimeofday(&t_end.tv,NULL);
    wlonglist.push_back((t_end.tv.tv_sec - t_start.tv.tv_sec)*1000000 + (t_end.tv.tv_usec - t_start.tv.tv_usec));
//    __asm__ volatile ("rdtsc" : "=a" (t_end.LowPart), "=d" (t_end.HighPart));
#endif //_WIN32
    Switch = &speedTestClass::start;
	std::cout << memo << " finish." << std::endl;
}

// -------------------------------------------------------
//! \brief  測定中にshowTimeなどの関数を呼んだ時に強制的に測定を止める.
/*! */
//! \param  なし.
//! \return なし.
inline void speedTestClass::forceStop(void)
{
    if(Switch == &speedTestClass::stop){    //!< 時間計測が動いている時
        stop("");
        std::cout << "speed test stop." << std::endl;
    }
}

// -------------------------------------------------------
//! \brief  参照の値が正しいかチェック.
/*! 正しくない場合は例外をスローする */
//! \param  index  チェックする参照番号.
//! \return なし.
inline void speedTestClass::checkTimeIndex(unsigned int &index)
{
    if(index >= wlonglist.size()){
		index = 0;
		std::cerr << __FUNCTION__ << std::endl << "Invalid value index. set index = 0." << std::endl;
    }
}

// -------------------------------------------------------
//! \brief  参照の値が正しいかチェック.
/*! 正しくない場合は例外をスローする */
//! \param  index  チェックする名前.
//! \return なし.
inline unsigned long speedTestClass::checkTimeIndex(std::string &name)
{
    unsigned long index;
    for(index=0; index<stringlist.size(); ++index){
        if(stringlist[index] == name){
            goto LOOP_SUCCESS;
        }
    }
	index = 0;
	std::cerr << __FUNCTION__ << std::endl << "Invalid value index. set index = 0." << std::endl;
LOOP_SUCCESS:
    return index;
}





// -------------------------------------------------------
//! \brief  プライベートメンバの初期値を入れる.
//! \param  なし.
//! \return なし.
speedTestClass::speedTestClass():
    Switch(&speedTestClass::start),
    max_length(0)
{
    __CONSTRUCTOR;
#ifdef _WIN32
    //変数の初期化
    QueryPerformanceFrequency(&cpu_freq);            // !< Windowsでの計算の制度を入れる
#else
    cpu_freq.QuadPart = 1000*1000;
#endif //_WIN32
    if( cpu_freq.QuadPart > 0xffffffff )cpu_freq.QuadPart = 0xffffffff;   //!< エラー処理
}

// -------------------------------------------------------
//! \brief  特に何もしない.
//! \param  なし.
//! \return なし.
speedTestClass::~speedTestClass(void)
{
    __DESTRUCTOR;
}

// -------------------------------------------------------
//! \brief  指定したラップのタイムを取得する.
//! \param  index 何回目の計測か.
//! \return ラップタイムを返却する.
long double speedTestClass::getTime(unsigned int index)
{
    forceStop();
    checkTimeIndex(index);														// ラップの参照が正しいかをチェック
	return (long double)wlonglist[index]/(long double)cpu_freq.QuadPart;        // index番目のラップタイムを返却
}

// -------------------------------------------------------
//! \brief  指定したラップのタイムを取得する.
//! \param  name 保存した時の名前.
//! \return ラップタイムを返却する.
long double speedTestClass::getTime(std::string name)
{
    forceStop();
    return (long double)wlonglist[checkTimeIndex(name)]/(long double)cpu_freq.QuadPart;        // index番目のラップタイムを返却
}

// -------------------------------------------------------
//! \brief  指定したラップのタイムを表示する.
//! \param  index 何回目の計測か.
//! \return なし.
void speedTestClass::showTime(std::string name)
{
    forceStop();
    showTime(checkTimeIndex(name));
}

// -------------------------------------------------------
//! \brief  指定したラップのタイムを表示する.
//! \param  index 何回目の計測か.
//! \return なし.
inline void speedTestClass::showTime(unsigned int index)
{
    forceStop();
    checkTimeIndex(index);                      // ラップの参照が正しいかをチェック
    std::cout.setf(std::ios::fixed);            // 常に小数点を表示するようにセット
    if(stringlist[index] == ""){
        std::cout << "process(" << index << ") ";
        for(unsigned int i=1;i<max_length;++i){
            std::cout << " ";
        }
    }else{
        std::cout << "process(" << stringlist[index] << ") ";
        for(unsigned int i=(unsigned int)stringlist[index].length();i<max_length;++i){
            std::cout << " ";
        }
    }
    std::cout << "elapsed time: "<< (long double)wlonglist[index]/(long double)cpu_freq.QuadPart << " (sec)" << std::endl;
}

// -------------------------------------------------------
//! \brief  すべてのラップのタイムを表示する.
//! \param  なし.
//! \return なし.
void speedTestClass::showTimeAll(void)
{
    forceStop();

    max_length = 0;
    for(unsigned int i=0; i<stringlist.size(); ++i){
        if(stringlist[i].length()>max_length){
            max_length=(unsigned int)stringlist[i].length();
        };
    }

    for(unsigned int i=0; i<wlonglist.size(); ++i){
        showTime(i);
    }
    max_length=0;
}

// -------------------------------------------------------
//! \brief  保存されているすべてのタイムを削除.
//! \param  なし.
//! \return なし.
inline void speedTestClass::clearTime(unsigned int index)
{
    forceStop();
    checkTimeIndex(index);          // ラップの参照が正しいかをチェック
    stringlistit = stringlist.begin();
    wlonglistit  = wlonglist.begin();
    for(unsigned int i=0; i< index; ++i){
        ++stringlistit;
        ++wlonglistit;
    }
    stringlistit = stringlist.erase(stringlistit);
    wlonglistit  = wlonglist.erase(wlonglistit);
}

// -------------------------------------------------------
//! \brief  保存されているすべてのタイムを削除.
//! \param  なし.
//! \return なし.
inline void speedTestClass::clearTime(std::string name)
{
    forceStop();
    clearTime(checkTimeIndex(name));
}

// -------------------------------------------------------
//! \brief  保存されているすべてのタイムを削除.
//! \param  なし.
//! \return なし.
void speedTestClass::clearTimeAll(void)
{
    forceStop();
    wlonglist.clear();              // タイムをすべて消去する
    stringlist.clear();                // つけた名前をすべて消去する
}
