// ------------------------------------------------------------------------
/*
	author: Jaap Suter
   
	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_GRAPH_H
#define REVEAL_GRAPH_H

#include <stddef.h>
#include "reveal/impl/window.h"
#include "reveal/impl/gui_metrics.h"
#include "reveal/type_traits/type_traits.h"
#include "reveal/impl/util.h"

namespace Reveal {

// ------------------------------------------------------------------------------
/*!
	\brief Todo
*/
// ------------------------------------------------------------------------------
template < size_t N, class T >
class Graph : public Impl::Window
{
public:

	Graph( const char* name, Impl::Tree& tree, T defaultValue )
		: Impl::Window( name, tree ),
		  mFront( mBuffer ),
		  mMax( mBuffer ),
		  mMin( mBuffer ),
		  mDefault( defaultValue )
	{

		for ( size_t i = 0; i < N; ++i ) mBuffer[ i ] = defaultValue;
	}

	Graph( const char* name, Impl::Tree& tree, T lockedMin, T lockedMax )
		: Impl::Window( name, tree ),
		  mFront( mBuffer ),
		  mMax( 0 ),
		  mLockedMax( lockedMax ),		  
		  mMin( 0 ),
		  mLockedMin( lockedMin )
	{

		for ( size_t i = 0; i < N; ++i ) mBuffer[ i ] = (mLockedMax + mLockedMin) / T(2);
	}

	void AddValue( T t )
	{
		*mFront = t;

		// Do we want intelligent min and maxing, or the locked min and max;
		if ( mMax && mMin )
		{
			if ( mFront == mMax )
			{
				mMax = mBuffer;
				for ( size_t i = 0; i < N; ++i  ) 
					if ( mBuffer[ i ] > *mMax ) mMax = mBuffer + i;
			}
			else if ( t > *mMax ) mMax = mFront;
			
			if ( mFront == mMin )
			{
				mMin = mBuffer;
				for ( size_t i = 0; i < N; ++i ) 
					if ( mBuffer[ i ] < *mMin ) mMin = mBuffer + i;
			}
			else if ( t < *mMin ) mMin = mFront;
		}

		++mFront;
		if ( mFront > (mBuffer + N - 1) ) mFront = mBuffer;		
	}

private:

	virtual void DoRender( const Renderer& renderer, const GuiColors& guiColors ) const
	{
		renderer.Bar( X(), Y(), Width(), Height(), 0x66000000 );
		renderer.Bar( X() + Width() - 20, Y() + Height() - 20, 20, 20, 0xFF222222 );
		renderer.Bar( X(), Y(), Width(), Impl::GuiMetrics::GetRowHeight(), 0xFF888888 );
		renderer.String( X() + Impl::GuiMetrics::TEXT_BORDER_WIDTH, Y() + Impl::GuiMetrics::TEXT_BORDER_HEIGHT, Name(), guiColors.mTextColor );

		T min = mLockedMin;
		T max = mLockedMax;
		if ( mMax && mMin )
		{
			min = *mMin;
			max = *mMax;
		}

		T* item = mFront;

		int x = 0;
		int y = 0;
		int prevX = 0;
		int prevY = 0;
		for ( size_t i = 0; i < N; ++i, ++item )
		{
			if ( item > (mBuffer + N - 1) ) item = mBuffer;

			prevX = x;
			prevY = y;
			x = X() + (static_cast< int >( i ) * Width() / N);
			y = Y() + Height() - static_cast< int >( ((*item - min) * (Height() - Impl::GuiMetrics::GetRowHeight()) / (max - min + 1)) );
			if ( i > 0 ) renderer.Line( prevX, prevY, x, y, 0xFFFFFFFF );

			if ( i == (N - 1) )
			{
				const int maxStringLength = 32;
				char valueRepr[ maxStringLength ];
				char maxRepr[ maxStringLength ];
				char minRepr[ maxStringLength ];
				if ( TypeTraits::IsIntegral< T >::value )
				{
					Util::ToString( static_cast< int >( max ), maxRepr, maxStringLength );
					Util::ToString( static_cast< int >( min ), minRepr, maxStringLength );
					Util::ToString( static_cast< int >( *item ), valueRepr, maxStringLength );
				}
				else
				{
					Util::ToString( static_cast< float >( max ), maxRepr, maxStringLength );
					Util::ToString( static_cast< float >( min ), minRepr, maxStringLength );
					Util::ToString( static_cast< float >( *item ), valueRepr, maxStringLength );
				}
				renderer.String( X() + Width() + 10 /* fix */, y, valueRepr, 0xFFFFFFFF );
				renderer.String( X() + Width() - 80 /* fix */, Y() + Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::TEXT_BORDER_HEIGHT, maxRepr, 0xFFFFFFFF );
				renderer.String( X() + Width() - 80 /* fix */, Y() + Height() - Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::TEXT_BORDER_HEIGHT, minRepr, 0xFFFFFFFF );
			}
		}

		Window::DoRender( renderer, guiColors );
	}

	Graph( const Graph& ); // Not implemented, non-copyable;
	Graph& operator = ( const Graph& ); // Not implemented, non-assignable;

	virtual size_t  DoToString( const char* path, char* buffer, size_t size ) const 
	{
		// Does nothing (can't save a graph);
		return 0;
	}

	// Attributes;

	mutable T* mFront;
	mutable T  mBuffer[ N ];

	T*   mMax;
	T    mLockedMax;
	T*   mMin;
	T    mLockedMin;
	T    mDefault;

};

} // Namespace Reveal;

#endif
