/*
 * Code created by Thomas Whittaker (RT) for a FreeSpace 2 source code project
 *
 * You may not sell or otherwise commercially exploit the source or things you 
 * created based on the source.
 *
*/

/*
 * This module is intended as a debug tool to measure the speed of blocks of code and display
 * that information as coloured bars across the top of the screen.
 *
 * This allows interactive measuring of components of the code, very useful for optimisztion.
 */

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

#include "timerbar.h"
#include "globalincs/pstypes.h"


// Internal structure for handling frame data
typedef	struct
{
	LARGE_INTEGER start_value;
	LARGE_INTEGER total_value;

	LARGE_INTEGER average;
	LARGE_INTEGER frame_total;

} timerbar_profile;

timerbar_profile profiles[MAX_NUM_TIMERBARS];
int timerbar_current_profile = 0;

LARGE_INTEGER timerbar_ultimate_start_value;
LARGE_INTEGER timerbar_last_start_value;

// Data needed for push and pop functionality
const int MAX_TB_STACK_SIZE = 100;

int timerbar_stack[MAX_TB_STACK_SIZE];
int timerbar_current_stack_layer = 0;

/**
 * Pssst, hey, you. Ya wanna timerbar? Real cheap.
 *
 * @param value Index count value (?)
 */
void timerbar_push(int value)
{
	if(timerbar_current_stack_layer >= MAX_TB_STACK_SIZE)
	{
		// To many pushs, this must be a bug
		// Someone is letting code miss the pops, check for early retuns
		Assert(!(timerbar_current_stack_layer >= MAX_TB_STACK_SIZE));
		return;
	}

	timerbar_stack[timerbar_current_stack_layer] = timerbar_current_profile;
	timerbar_current_stack_layer++;

	timerbar_switch_type(value);
}

/*
 */
void timerbar_pop()
{
	timerbar_current_stack_layer--;
	if(timerbar_current_stack_layer < 0)
	{
		Assert(!(timerbar_current_stack_layer < 0));
		timerbar_current_stack_layer = 0;
		return;
	}

	timerbar_switch_type(timerbar_stack[timerbar_current_stack_layer]);
}

/**
 * This pointer holds the draw function to use or NULL
 *
 * @param colour Colour index (>=0)
 * @param x Coordinate position
 * @param y Coordinate position
 * @param w element width
 * @param h element height
 */
void (*draw_func_ptr)(int colour, float x, float y, float w, float h) = NULL;

/*
 */
void timerbar_start_frame()
{
	timerbar_current_stack_layer = 0;
	timerbar_current_profile     = 0;

	if(QueryPerformanceCounter(&timerbar_ultimate_start_value) == FALSE)
	{
		// DBUGFILE_OUTPUT_0("QueryPerformanceCounter not supported by hardware");
		draw_func_ptr = NULL;
	}

	timerbar_last_start_value = timerbar_ultimate_start_value;

}

// Constants need for timerbar_conv_and_draw calculations
const int WIDTH = 480;
const float WIDTHF = ((float) WIDTH) * 1.01f;

/**
 * Internal conversion function to convert draw data into bars for the screen
 *
 * @param colour Colour index (>=0)
 * @param xpos   X position to start bar on
 * @param xwidth Width of bar
 * @param yrow	 Index of row bar belongs to
 */
void timerbar_conv_and_draw(int colour, int xpos, int xwidth, int yrow)
{
	float fxpos  = (float) xpos;
	float fxwidth = (float) xwidth;

	draw_func_ptr(colour,
		fxpos / WIDTHF,
		yrow * 0.01f,
		fxwidth / WIDTHF,
		0.005f);
}

/*
 */
void timerbar_end_frame()
{
	// Now we want to draw the bars
	if(draw_func_ptr == NULL)
	{
		// we have not been given a draw function so we are going to have to quit
		return;
	}

	timerbar_switch_type(-1);

	int last_xpos = 0;
	int y_pos     = 0;


	for(int i = 0; i < MAX_NUM_TIMERBARS; i++)
	{
		// Nothing to render this frame
		if(profiles[i].frame_total.QuadPart == 0)
		{
			continue;
		}

		int xsize = (int) profiles[i].frame_total.QuadPart / 90;

		// break condition in else
		while(1)
		{
			// if bar runs over
			if(last_xpos + xsize > WIDTH)
			{
				int rem_len = WIDTH - last_xpos;

				// Draw from current position to the end of the bar
				timerbar_conv_and_draw(i, last_xpos, rem_len, y_pos);

				xsize -= rem_len;
				last_xpos = 0;
				y_pos++;
			}
			else
			{
				// Draw whats left and break for the next bar
				timerbar_conv_and_draw(i,last_xpos, xsize, y_pos);

				last_xpos += xsize;
				break;
			}
		}

		// reset for next frame
		profiles[i].frame_total.QuadPart = 0;
	}
}

/**
 * Not entirely sure what this does.
 *
 * @param num Probably a pretend boolean
 */
void timerbar_switch_type(int num)
{
	if(num >= MAX_NUM_TIMERBARS)
	{
		return;
	}

	// Calculate old profile's total
	LARGE_INTEGER now;
	if(QueryPerformanceCounter(&now) == FALSE)
	{
		// DBUGFILE_OUTPUT_0("QueryPerformanceCounter not supported by hardware");
	}

	profiles[timerbar_current_profile].frame_total.QuadPart += 
		(now.QuadPart - timerbar_last_start_value.QuadPart);

	if(num != -1)
	{
		// Switch to new profile
		timerbar_current_profile = num;
	}

	// Update time to count from
	if(QueryPerformanceCounter(&timerbar_last_start_value) == FALSE)
	{
		// DBUGFILE_OUTPUT_0("QueryPerformanceCounter not supported by hardware");
	}
}

/**
 * Clears the instance of the timer bar
 *
 * Look at the parameters for "void (*draw_func_ptr)"
 * They directly equate the new_draw_func_ptr, but we can't
 * place params for a double stuffed function like this.
 */
void timerbar_set_draw_func(void (*new_draw_func_ptr)(int colour, float x, float y, float w, float h))
{
	draw_func_ptr = new_draw_func_ptr;
}

