//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"
//#include "VWTiming.h"

#include "VWUI/VWUI.h"

#ifndef _WINDOWS
# include <time.h>	// timing for Mac
#endif


using namespace VWFC::Tools;
using namespace VectorWorks::VWUI;

// ------------------------------------------------------------------------------------------------

VWTiming::STimerStart::STimerStart()
{
	startTime = 0;
}

VWTiming::STimerStart::~STimerStart()
{
	startTime = 0;
}

// ------------------------------------------------------------------------------------------------

VWTiming::SData::SData()
{
	data = 0;
}

VWTiming::SData::SData(size_t dt, const TXString& txt)
{
	data = dt;
	desc = txt;
}

VWTiming::SData::~SData()
{
	data = 0;
}

// ------------------------------------------------------------------------------------------------

VWTiming::STimerCollected::STimerCollected()
{
	timeEllapsed = 0;
}

VWTiming::STimerCollected::~STimerCollected()
{
	timeEllapsed = 0;
}

// ------------------------------------------------------------------------------------------------

VWTiming::STimerCategory::STimerCategory()
{
}

VWTiming::STimerCategory::~STimerCategory()
{
}

// ------------------------------------------------------------------------------------------------
VWTiming* VWTiming::spInstance	= NULL;

VWTiming::VWTiming()
{
}

VWTiming::~VWTiming()
{
}

VWTiming* VWTiming::GetInstance()
{
	// you should init the timer first
	VWFC_ASSERT( spInstance != NULL );
	return spInstance;
}

void VWTiming::Initialize()
{
	VWFC_ASSERT( spInstance == NULL );
	if ( spInstance == NULL )
		spInstance	= NEW_OBJECT( VWTiming );
}

void VWTiming::Deinitilalize()
{
	VWFC_ASSERT( spInstance != NULL );
	if ( spInstance != NULL )
		DELETE_OBJECT( VWTiming, spInstance );
}

UInt32 VWTiming::GetTimer()
{
	UInt32		time	= 0;
#ifdef _WINDOWS
	time		= (UInt32) ::GetTickCount();
#else
	time		= ::TickCount() * 17;	// Mac returns 1 /60th of a second
#endif
	return time;
}

void VWTiming::Start(const TXString& category, const TXString& context)
{
	STimerStart	start;
	start.category		= category;
	start.context		= context;
	start.startTime		= this->GetTimer();

	fTimerStartQueue.push_back( start );
}

void VWTiming::End(const TXString& taskInfo, STimerCollected& collected)
{
	UInt32		thisTime	= this->GetTimer();

	STimerStart	started		= fTimerStartQueue.back();
	fTimerStartQueue.pop_back();

	collected.context		= started.context;
	collected.taskInfo		= taskInfo;
	collected.timeEllapsed	= thisTime - started.startTime;

	STimerCategory*	pFoundCategory		= NULL;
	// find if the category already exist
	size_t		len			= fTimerCategories.size();
	for(size_t i=0; i<len; i++) {
		STimerCategory&		category	= fTimerCategories[ i ];
		if ( category.category == started.category )  {
			pFoundCategory		= & category;
			break;
		}
	}

	// if found -- add to existring
	// otherwise create new category
	if ( pFoundCategory ) {
		pFoundCategory->arrTimerData.push_back( collected );
	}
	else {
		STimerCategory		newCategory;
		newCategory.category		= started.category;
		newCategory.arrTimerData.push_back( collected );

		fTimerCategories.insert( fTimerCategories.begin(), newCategory );
	}
}

void VWTiming::End(const TXString& taskInfo)
{
	STimerCollected	collected;
	this->End( taskInfo, collected );
}

void VWTiming::End(const TXString& taskInfo, size_t data1, const TXString& str1)
{
	STimerCollected	collected;
	collected.arrData.push_back( SData( data1, str1 ) );
	this->End( taskInfo, collected );
}

void VWTiming::End(const TXString& taskInfo, size_t data1, const TXString& str1, size_t data2, const TXString& str2)
{
	STimerCollected	collected;
	collected.arrData.push_back( SData( data1, str1 ) );
	collected.arrData.push_back( SData( data2, str2 ) );
	this->End( taskInfo, collected );
}

void VWTiming::End(const TXString& taskInfo, size_t data1, const TXString& str1, size_t data2, const TXString& str2, size_t data3, const TXString& str3)
{
	STimerCollected	collected;
	collected.arrData.push_back( SData( data1, str1 ) );
	collected.arrData.push_back( SData( data2, str2 ) );
	collected.arrData.push_back( SData( data3, str3 ) );
	this->End( taskInfo, collected );
}

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Timing dialog
namespace VWFC {
	namespace Tools {
		class CTimingResultsDialog : public VWDialog 
		{
			const VWTiming::TTimerCategoriesArray&	fArrCategories;
			TXString								fstrDescription;

			VWTabCtrl					fTabCtrl;
			struct SCategories {
				size_t						fTimerCatIndex;
				VWTabPaneCtrl*				fpTabPaneCtrl;
				VWListBrowserCtrl*			fpListBoxCtrl;
			};
			typedef std::vector<SCategories>		TDlgCategoriesArray;

			TDlgCategoriesArray					farrCategories;

		public:
			// -----------------------------------------------------------------------
			CTimingResultsDialog(const VWTiming::TTimerCategoriesArray& categories, const TXString& desc)
				: fArrCategories( categories ), fTabCtrl( 3 )
			{
				fstrDescription		= desc;
			}

			// -----------------------------------------------------------------------
			~CTimingResultsDialog()
			{
				size_t	len		= farrCategories.size();
				for(size_t i=0; i<len; i++) {
					SCategories& cat	= farrCategories[i];
					DELETE_OBJECT( VWTabPaneCtrl, cat.fpTabPaneCtrl );
					DELETE_OBJECT( VWListBrowserCtrl, cat.fpListBoxCtrl );
				}
			}

			// -----------------------------------------------------------------------
			virtual bool CreateDialogLayout()
			{
				TXString		dlgTitle	= "Timing Results : ";
				dlgTitle					+= fstrDescription;
				if ( ! this->CreateDilaog( dlgTitle, "OK", "", false ) )			return false;

				if ( ! fTabCtrl.CreateControl( this ) )								return false;

				size_t	ctrlID		= 4;
				// create controls
				size_t	len			= fArrCategories.size();
				for(size_t i=0; i<len; i++) {
					const VWTiming::STimerCategory& cat	= fArrCategories[ i ];

					SCategories		dlgCat;
					dlgCat.fTimerCatIndex		= i;
					dlgCat.fpTabPaneCtrl		= NEW_OBJECT( VWTabPaneCtrl )( ctrlID++ );
					if ( dlgCat.fpTabPaneCtrl && ! dlgCat.fpTabPaneCtrl->CreateControl( this, cat.category ) )	return false;

					dlgCat.fpListBoxCtrl		= NEW_OBJECT( VWListBrowserCtrl )( ctrlID++ );
					if ( dlgCat.fpListBoxCtrl && ! dlgCat.fpListBoxCtrl->CreateControl( this, 90, 15 ) )		return false;

					farrCategories.push_back( dlgCat );
				}

				// start arranging
				this->AddFirstGroupControl( & fTabCtrl );

				// arrange controls
				len					= farrCategories.size();
				for(size_t i=0; i<len; i++) {
					const SCategories& cat	= farrCategories[i];

					fTabCtrl.AddPane( cat.fpTabPaneCtrl );
					cat.fpTabPaneCtrl->AddFirstGroupControl( cat.fpListBoxCtrl );
				}

				return true;
			}

			// -----------------------------------------------------------------------
			virtual	void			OnDDXInitialize()
			{
			}

			// -----------------------------------------------------------------------
			virtual void			OnSetUpEvent()
			{
				size_t	len			= farrCategories.size();
				for(size_t i=0; i<len; i++) {
					const SCategories&				cat		= farrCategories[i];
					const VWTiming::STimerCategory& timeCat	= fArrCategories[ cat.fTimerCatIndex ];

					cat.fpListBoxCtrl->InsertColumn( 0, "taskInfo", 200 );
					VWListBrowserColumn	timeCol	= cat.fpListBoxCtrl->InsertColumn( 1, "time", 75 );
					cat.fpListBoxCtrl->InsertColumn( 2, "context", 100 );
					cat.fpListBoxCtrl->InsertColumn( 3, "data", 150 );
					timeCol.SetColumnHeaderJust( kColumnHeaderJustificationCenter );
				
					size_t	len			= timeCat.arrTimerData.size();
					// dont allow more than 100 time results
					// we dont want to overflow the lists max (32768)
					if ( len > 100 ) {
						// show first items only
						len	= 100;
					}
					for(size_t i=0; i<len; i++) {
						const VWTiming::STimerCollected&	timer	= timeCat.arrTimerData[ i ];

						double		time		= ((double) timer.timeEllapsed) / 1000;
						TXString	strTime;	strTime.Format( "%.5f sec", time );
						TXString	strData;
						size_t		dataLen		= timer.arrData.size();
						for(size_t j=0; j<dataLen; j++) {
							const VWTiming::SData&	data	= timer.arrData[j];

							TXString	str;
							str.Format( "(%d %s)", data.data, (const char*)data.desc );
							strData		+= str;
						}

						size_t					itemIndex	= cat.fpListBoxCtrl->AddRow( timer.taskInfo );

						cat.fpListBoxCtrl->GetItem( itemIndex, 1 ).SetItemText( strTime );
						cat.fpListBoxCtrl->GetItem( itemIndex, 2 ).SetItemText( timer.context );
						cat.fpListBoxCtrl->GetItem( itemIndex, 3 ).SetItemText( strData );

						//cat.fpListBoxCtrl->SetItemTextJustification( itemIndex, 1, kListBrowserTextJustificationRight );
					}				
				}
			}
		};
	}
};


void VWTiming::ShowTimingResultDialog(const TXString& description)
{
	CTimingResultsDialog	dlg( fTimerCategories, description );
	dlg.RunDialogLayout();
}

