/*****************************************************************************
 * $Id: eng_debug.cc,v 1.25 2006/07/23 00:44:38 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Debug/profiling support, statistics, etc.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#if DEBUG

#define ENG_DEBUG_CC 1
#include <sys/time.h>
#include "eng.hh"
#include "eng_debug.hh"
#include "eng_init.hh"
#include "eng_polygon.hh"
#include "gfx_stream.hh"
#if __GLIBC__
#include <malloc.h>
#endif

#if STATS
ostream* gStreamStats = &cout;
#endif

/*****************************************************************************
 * Return total amount of bytes allocated.
 *****************************************************************************/
uint
MemUsed( void )
{
#if __GLIBC__
	struct mallinfo meminfo;
	meminfo = mallinfo();
	return (uint)meminfo.arena;
#else
    return 0;
#endif
}

/*****************************************************************************
 * Stats constructor.
 *****************************************************************************/
EngineStats::EngineStats( void )
{
	mPolygonAttribs			= 0;
	mCacheMissVertexs		= 0;
	mCacheMissNormals		= 0;
	mObjectGroupVisible		= 0;
	mObjectGroupInvisible	= 0;
}

/*****************************************************************************
 * Reset some stats prior to drawing views.
 *****************************************************************************/
void
EngineStats::PreDrawViews( void )
{
	mObjectGroupVisible		= 0;
	mObjectGroupInvisible	= 0;
}

/*****************************************************************************
 * operator<<(EngineStats)
 *****************************************************************************/
ostream&
operator<<( ostream& strm, const EngineStats& o )
{
	extern uint DebugSharedPolygonAttribsCnt( void );

    strm << "Statistics: \n"
         << "  shared PolygonAttribs      = " << DebugSharedPolygonAttribsCnt() << '\n'
         << "  PolygonAttribs [1]         = " << o.mPolygonAttribs				<< '\n'
         << "  cache miss vertexs         = " << o.mCacheMissVertexs			<< '\n'
         << "  cache miss normals         = " << o.mCacheMissNormals			<< '\n'
         << "[1] Count is inflated by SharedVal storing two copies (key,val)"; // << '\n'
    return strm;
}

/*****************************************************************************
 * Test and report the rate of frames per second (FPS).
 *****************************************************************************/
void
Engine::TestFrameRate( void )
{
	// Results are output into a string and printed after rendering is done
	// (to avoid skewing results by cout which is slow).
	string resultsPrecached; TestFrameRateSys( resultsPrecached );
	string resultsCached;    TestFrameRateSys( resultsCached );
	*gStreamDebug << endl << "pre-cached:" << endl << resultsPrecached << endl;
	*gStreamDebug << endl << "cached:    " << endl << resultsCached    << endl;
#ifdef _WIN32
	mTestFrameRate = false;
#else
	Exit(0);
#endif
}

// System-specific subroutine of TestFrameRate().
void
Engine::TestFrameRateSys( string& result )
{
#if __linux__
	TestFrameRate_times( result );
#else
	//TestFrameRateMeasure_clock( result );
	TestFrameRate_gettimeofday( result );
#endif
}

// Based on POSIX times().
// Measures FPS and reports time consumed in user and kernel modes.
#if __linux
void
Engine::TestFrameRate_times( string& result )
{
	struct tms time1; times( &time1 );
	TestFrameRateRender();
	struct tms time2; times( &time2 );
	fp clocksPerSec = sysconf( _SC_CLK_TCK );
	fp secUserSys   = fp(time2.tms_utime+time2.tms_stime - time1.tms_utime+time1.tms_stime) / clocksPerSec;
	fp secUser		= fp(time2.tms_utime                 - time1.tms_utime                ) / clocksPerSec;
	fp fps			= fp(mView.GetTrueFrameCnt()) / secUserSys;
	ostringstream oss;
	oss << "------------------------------------\n"
	    << "fps             = " << fps						<< "\n"
	    << "secs (user)     = " << secUser					<< "\n"
	    << "secs (user+sys) = " << secUserSys				<< "\n"
	    << "frames          = " << mView.GetTrueFrameCnt()	<< "\n"
        << "------------------------------------\n";
	result = oss.str();
}
#else // ! __linux__
// Based on gettimeofday().  Measures FPS.
// Can't report the percentage of time consumed in user/kernel modes.
void
Engine::TestFrameRate_gettimeofday( string& result )
{
	struct timeval tv1; gettimeofday( &tv1, NULL );
	TestFrameRateRender();
	struct timeval tv2; gettimeofday( &tv2, NULL );
	fp ftv1	 = fp(tv1.tv_sec + tv1.tv_usec) / 1000000.0;	// sec + usec
	fp ftv2	 = fp(tv2.tv_sec + tv2.tv_usec) / 1000000.0;	// sec + usec
	fp delta = ftv2 - ftv1;
	fp fps   = fp(mView.GetTrueFrameCnt()) / delta;
	ostringstream oss;
	oss << "--------------------------\n"
	    << "fps    = " << fps						<< "\n"
	    << "secs   = " << delta						<< "\n"
	    << "frames = " << mView.GetTrueFrameCnt()	<< "\n"
        << "---------------------------\n";
	result = oss.str();
}
#endif // __linux__

// Renders a frame.
void
Engine::TestFrameRateRender( void )
{
	const int framesToTest = 200; //100
	const fp deg = 6.0;
	mView.ResetFrames();
	mEye.Translate( AXIS_Y, WORLD(-1000.0) );
	for ( int n = 0; n < framesToTest; ++n )
	{
		Render();
		if ( n % 50 < 15 )
			mEye.Rotate( AXIS_Y, deg );
		else
			mEye.Translate( AXIS_Z, WORLD(-2000.0) );
	}
}

/*****************************************************************************
 * For testing frame-rate.
 *****************************************************************************/
void
Engine::TickTestFrameRate( void )
{
	// Let about one second worth of frames elapse before testing FPS.
	// This gives the gfxsys time to become ready.
	static int ticksFPS = 0;
	if ( ++ticksFPS >= 50 )
	{
		static bool testingFrameRate = false;
		if ( ! testingFrameRate )
		{
			testingFrameRate = true;
			TestFrameRate(); // causes program to exit
		}
	}
}

/*****************************************************************************
 * Handler for when an assertion fails.
 *****************************************************************************/
void
EngineAssertHandler( bool cond, const char* fileName, int fileLine )
{
	if ( UX( ! cond ) )
	{
		cerr << "** ASSERTION FAILED **" << endl
             << "** " << fileName << ':' << fileLine << " **" << endl
             << "Object being rendered = '" << gEng.GetObjectNameRendering() << '\'' << endl
             << "seed = 0x" << hex << gEng.GetSeed() << endl;
		abort();
	}
}

#endif // DEBUG
