/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef DISPLAY_H
#define DISPLAY_H

//=====================================================================================================================
//=====================================================================================================================

namespace Game
{
	//=================================================================================================================
	//=================================================================================================================

	class Window;
	class RenderTarget;
	class DepthBuffer;
	class VertexBuffer;
	
	//=================================================================================================================
	/// @brief		Represents a Direct3D9 display for a window 
	/// @remarks	This object relies on the window existing for it's existance. If the window is deleted then this is
	///				invalid.
	//=================================================================================================================

	class Display : public ObjectBase
	{
		public:
		
			//=============================================================================================================
			// Friends
			//=============================================================================================================
			
				/// DisplayEvictable is a friend of this class and needs private access
				
				friend class DisplayEvictable;
		
			//=============================================================================================================
			// Enums
			//=============================================================================================================
			
				/// Level of anti-aliasing used by the application or by a render target / depth buffer:
				
				enum AntialiasingLevel
				{
					/// Do no antialiasing
				
					ANTIALIAS_NONE = D3DMULTISAMPLE_NONE ,
					
					/// Do 2 sample AA
					
					ANTIALIAS_2_SAMPLES	= D3DMULTISAMPLE_2_SAMPLES ,
					
					/// Do 4 sample AA
					
					ANTIALIAS_4_SAMPLES	= D3DMULTISAMPLE_4_SAMPLES ,
					
					/// Do 8 sample AA
					
					ANTIALIAS_8_SAMPLES	= D3DMULTISAMPLE_8_SAMPLES ,
					
					/// Do 16 sample AA
					
					ANTIALIAS_16_SAMPLES = D3DMULTISAMPLE_16_SAMPLES	
				};			
			
				/// Primitive type for drawing:
				
				enum PrimitiveType
				{
					PT_POINTS			= D3DPT_POINTLIST		,
					PT_LINES			= D3DPT_LINELIST		,
					PT_LINESTRIP		= D3DPT_LINESTRIP		,
					PT_TRIANGLES		= D3DPT_TRIANGLELIST	,
					PT_TRIANGLE_STRIP	= D3DPT_TRIANGLESTRIP	,
					PT_TRIANGLE_FAN		= D3DPT_TRIANGLESTRIP
				};

			//=============================================================================================================
			// Functions
			//=============================================================================================================

				/// Creates the directx9 display: 

				Display( Window * parent , bool fullscreen , Vec2u fullscreen_size , AntialiasingLevel aa_level = ANTIALIAS_NONE );

				/// Destroys the directx9 display:

				~Display();
				
				/// Tells if the display needs a reset
				
				bool RequiresReset();

				/// Clear the screen, depth and stencil buffers: if display is bound to custom render targets and depth buffers then these are cleared

				void Clear( const Color & color , unsigned char stencil , float depth );
				
				/// Swap the back buffer onto the front buffer:

				void Present();

				/// Tells if the display is ok:

				bool IsValid(){ return m_d3d_device != 0; }
				
				/// Tells the object to announce it's type name: this is the type name without the Game:: namespace

				virtual const char * GetTypeName() const;					
				
				//=====================================================================================================
				/// @brief		Tells if the display is in fullscreen or not:
				/// @returns	True if fullscreen
				/// @pre		The display must be valid
				//=====================================================================================================
				
				bool IsFullscreen()
				{
					// Make sure ok:
				
					ENSURE( IsValid() , "The display must be valid" )
					
					// Return if fullscreen:
					
					return m_presentation_params.Windowed == FALSE;
				}
				
				/// Begins drawing: must be called before rendering.
				
				void BeginDraw();
				
				/// Ends drawing: must be called after rendering
				
				void EndDraw();
				
				/// Sets the default render state, which is optimised for the application in question
				
				void SetDefaultRenderState();
				
				/// Gets the maximum level of antialiasing that is supported by the graphics device
				
				AntialiasingLevel GetMaxmimumAALevel();						
				
				/// Gets the current level of anti-aliasing which is applied to the display:
				
				inline AntialiasingLevel GetAALevel(){ return m_aa_level; }
				
				//=========================================================================================================
				/// @brief		Draws a set of primitives from memory:
				/// @param		p - type of primitive to draw
				/// @param		data - primitive data stream
				/// @param		count - number of primitives to draw
				/// @pre		Display must be valid
				/// @pre		Data given must not be null
				/// @pre		Primitive count must not be zero
				/// @post		Draw operation must be successful
				//=========================================================================================================
				
				template<class T> inline void DrawUserPrimitives( PrimitiveType p , const T * data , unsigned count )
				{
					// Make sure ok:
					
					ENSURE( IsValid()	, "Display must be valid"				);
					ENSURE( data != 0	, "Data given must not be null"			);
					ENSURE( count != 0	, "Primitive count must not be zero"	);
					
					// Draw it:
					
					#ifdef DEBUG
					
						// Try:
					
						HRESULT result = m_d3d_device->DrawPrimitiveUP( (D3DPRIMITIVETYPE) p , count , (void*) data , sizeof(T) );
						
						// Make sure ok:
						
						ENSURE( result == D3D_OK , "Draw operation must be successful" )
					
					#else
					
						m_d3d_device->DrawPrimitiveUP( (D3DPRIMITIVETYPE) p , count , (void*) data , sizeof(T) );
					
					#endif
				}
				
				//=========================================================================================================
				/// @brief		Sets a given render state.
				/// @param		r - integer representing the render state to set
				/// @param		v - value of the render state
				/// @pre		Display must be valid
				//=========================================================================================================
				
				inline void SetRenderState( unsigned r , unsigned v )
				{
					// Make sure ok:
					
					ENSURE( IsValid() , "Display must be valid" );
					
					// Set:
					
					m_d3d_device->SetRenderState( (D3DRENDERSTATETYPE) r , (DWORD) v );
				}
				
				//=========================================================================================================				
				/// @brief		Gets the maximum texture width
				/// @returns	Maximum texture width
				/// @pre		The display must be valid
				//=========================================================================================================
				
				inline unsigned GetMaxTextureWidth()
				{ 
					// Make sure ok:
					
					ENSURE( IsValid() , "The display must be valid" )
					
					// Return
				
					return m_d3d_caps.MaxTextureWidth; 
				}
				
				//=========================================================================================================				
				/// @brief		Gets the maximum texture height
				/// @returns	Maximum texture height
				/// @pre		The display must be valid
				//=========================================================================================================
				
				inline unsigned GetMaxTextureHeight()
				{ 
					// Make sure ok:
					
					ENSURE( IsValid() , "The display must be valid" )
					
					// Return
				
					return m_d3d_caps.MaxTextureHeight; 
				}	
				
				//=========================================================================================================
				///	@brief		Gets the Direct3D device belonging to the display
				///	@returns	The actual D3D device
				/// @pre		The display must be valid
				//=========================================================================================================

				inline IDirect3DDevice9 * GetDevice()
				{ 
					// Make sure ok:
					
					ENSURE( IsValid() == true , "The display must be valid" )
					
					// Return it:
				
					return m_d3d_device; 
				}
				
				//=========================================================================================================
				/// @brief		Gets the Direct3D object that creates displays and returns capabilities etc.
				/// @returns	The direct 3D object used by this display
				/// @pre		The display must be valid
				//=========================================================================================================
				
				inline IDirect3D9 * GetDirect3D()
				{ 
					// Make sure ok:
					
					ENSURE( IsValid() == true , "The display must be valid" )
					
					// Return it:				
				
					return m_d3d; 
				}			

				//=========================================================================================================
				///	@brief		Gets the Window device that the display uses
				///	@returns	The window
				//=========================================================================================================

				inline Window * GetWindow(){ return m_window; }
				
				/// Sets the render target to use for rendering. Set to null to use back buffer.
				
				void SetRenderTarget( RenderTarget * target );			
				
				/// Sets the depth buffer to use for rendering with a render target. Set to null to use back buffer depth buffer.
				
				void SetDepthBuffer( DepthBuffer * target );
				
				/// Sets the curren vertex buffer to use. Set to null to unbind.
				
				void SetVertexBuffer( VertexBuffer * buffer , unsigned stride );
				
				/// Returns the vertex buffer currently bound to the pipeline
				
				inline VertexBuffer * GetVertexBuffer(){ return m_current_vertex_buffer; }

		private:
		
			//=============================================================================================================
			// Friends
			//=============================================================================================================
			
				/// Window needs private access to create this. Device cannot be made without a window.
			
				friend class Window;

			//=============================================================================================================
			// Functions
			//=============================================================================================================

				/// Sets up a Direct3D display for the window if possible

				void Setup( bool fullscreen , AntialiasingLevel aa_level );

				/// Shuts down the Direct3D display with the window

				void Shutdown();
				
				/// Resets the Direct3D display with the window

				void Reset();
				
				/// Releases all evictable resources before a device reset if not done already
				
				void ReleaseEvictableResources();
				
				/// Recreates all evictable resources after a device reset has occured
				
				void RecreateEvictableResources();

				/// Copying is not allowed

				Display( Display & );

				/// Copying is not allowed

				Display & operator = ( Display & );

			//=============================================================================================================
			// Variables
			//=============================================================================================================

				/// Window the directx display belongs to

				Window * m_window;

				/// The Direct3D9 api object

				IDirect3D9 * m_d3d;	

				/// The Direct3D device itself

				IDirect3DDevice9 * m_d3d_device;
				
				/// Capabilities for the direct3d device:
				
				D3DCAPS9 m_d3d_caps;

				/// Current presentation parameters for the device:

				D3DPRESENT_PARAMETERS m_presentation_params;
				
				/// The back buffer used by the device:
				
				IDirect3DSurface9 * m_back_buffer;
				
				/// The depth buffer used with the back buffer
				
				IDirect3DSurface9 * m_depth_stencil;				
				
				/// Size of the display mode in fullscreen:
				
				Vec2u m_fullscreen_size;
				
				/// Head of the list of resources that can be evicted by the graphics device
				
				DisplayEvictable * m_evictable_head;
				
				/// Tail of the list of resources that can be evicted by the graphics device
				
				DisplayEvictable * m_evictable_tail;
				
				/// Set to true if we have released evictable resources before a device reset
				
				bool m_released_evictable_resources;
				
				/// Current level of anti-aliasing which is being applied to the display
				
				AntialiasingLevel m_aa_level;
				
				/// The current render target in use: if null then target is the backbuffer
				
				RenderTarget * m_current_target;
				
				/// The current depth buffer in use: if null then the depth buffer is that associated with the backbuffer
				
				DepthBuffer * m_current_depth_buffer;
				
				/// The current vertex buffer bound to the pipeline. If null then there is none.
				
				VertexBuffer * m_current_vertex_buffer;
	};

};

#endif 