/*
	Trampoline Sim.
	Copyright (c) 2009, 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.
*/

//=====================================================================================================================
/// @brief		Represents a depth buffer that can be used in conjunction with a depth buffer.
/// @remarks	Depth buffer data is volatile and will be lost after a device reset. 
//=====================================================================================================================

#include "Precompiled.h"
#include "Graphics\DepthBuffer.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates the depth buffer with the given size and format
/// @param		display - display to use for the depth buffer
/// @param		size - size of the depth buffer
/// @param		format - format of the depth buffer
/// @param		aa_level - level of anti-aliasing to use for the depth buffer
/// @param		clamp_to_max_texure_size - if true then the depth buffer will automatically clamp the size of the 
///				depth buffer to the maximum texture size. Otherwise if false, then the creation may fail if the 
///				requested size is too big for the hardware.
/// @pre		The given size must be valid
/// @remarks	The given multi-sample quality may or may not be used depending on hardware support.
///				The depth buffer will attempt to best match the given setting however.
//=====================================================================================================================

DepthBuffer::DepthBuffer( Display * display , const Vec2u & size , Format format , Display::AntialiasingLevel aa_level , bool clamp_to_max_texure_size )
:
DisplayEvictable(display)
{
	// Make sure ok:
	
	ENSURE( size.x > 0 , "The given size must be valid" )
	ENSURE( size.y > 0 , "The given size must be valid" )

	// Initialiase everything:
	
	m_surface_format	= format;
	m_size				= size;
	m_aa_level			= aa_level;
	m_surface			= 0;
	
	// See if we are to validate the size:
	
	if ( clamp_to_max_texure_size )
	{
		if ( m_size.x > GetDisplay()->GetMaxTextureWidth() )
		{
			m_size.x = GetDisplay()->GetMaxTextureWidth();
		}
		
		if ( m_size.y > GetDisplay()->GetMaxTextureHeight() )
		{
			m_size.y = GetDisplay()->GetMaxTextureHeight();
		}		
	}	
	
	// Recreate the depth buffer:
	
	RecreateEvictableResources();
}

//=====================================================================================================================
/// @brief		Destructor: releases the depth buffer
//=====================================================================================================================

DepthBuffer::~DepthBuffer()
{
	// Release our resources if valid:
	
	if ( IsValid() )
	{
		DepthBuffer::ReleaseEvictableResources();
	}
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * DepthBuffer::GetTypeName() const
{
	return "DepthBuffer";
}

//=====================================================================================================================
/// @brief		Releases the depth buffer before a device reset: not to be called by anything other than the display!
/// @pre		The depth buffer must be valid
//=====================================================================================================================

void DepthBuffer::ReleaseEvictableResources()
{
	// Make sure ok:

	ENSURE( m_is_valid , "The depth buffer must be valid" )
	
	// Release our depth buffer surface:
	
	m_surface->Release(); m_surface = 0;
	
	// No longer valid:
	
	m_is_valid = false;
}

//=====================================================================================================================
/// @brief		Recreates the depth buffer after a device reset: not to be called by anything other than the display!
/// @pre		The depth buffer must have no resources on hand before the call
//=====================================================================================================================

void DepthBuffer::RecreateEvictableResources()
{
	// Make sure ok:

	ENSURE( m_surface == 0 && m_is_valid == false , "The depth buffer must have no resources on hand before the call" )
	
	// Determine the maximum aa level:
	
	Display::AntialiasingLevel max_aa_level = GetMaxAALevel();
	
	// See if we are above it:
	
	if ( (unsigned)(m_aa_level) > (unsigned)(m_aa_level) )
	{
		// Use the max aa level instead:
		
		m_aa_level = max_aa_level;
	}
	
	// Get the direct3d device:
	
	IDirect3DDevice9 * device = GetDisplay()->GetDevice();
	
	// Get the number of quality levels for this aa level.. What the hell is this about?
	
	DWORD quality_levels = 0; 
	
	GetDisplay()->GetDirect3D()->CheckDeviceMultiSampleType
	(
		D3DADAPTER_DEFAULT						,
		D3DDEVTYPE_HAL							,
		(D3DFORMAT) m_surface_format			,
		GetDisplay()->IsFullscreen() == false	,
		(D3DMULTISAMPLE_TYPE) m_aa_level		,
		&quality_levels
	);
	
	// Now make the depth buffer surface:
	
	HRESULT result = device->CreateDepthStencilSurface
	(
		m_size.x							,
		m_size.y							,
		(D3DFORMAT)	m_surface_format		,
		(D3DMULTISAMPLE_TYPE) m_aa_level	,
		quality_levels - 1					,
		true								,	// can discard contents after swap
		&m_surface							,
		0										// shared handle: only on vista
	);
	
	// See if that was ok:
	
	if ( result == D3D_OK && m_surface )
	{
		// All good:
			
		m_is_valid = true;
	}	
	else
	{
		// Not ok: if the surface is there then remove it
		
		if ( m_surface ) m_surface->Release();
		
		// Done with this:
		
		m_surface = 0;
	}
}

//=====================================================================================================================
/// @brief		Determines the maximum anti-aliasing level supported by the settings for the depth buffer
/// @returns	The maximum aa level that can be used by this depth buffer
//=====================================================================================================================

Display::AntialiasingLevel DepthBuffer::GetMaxAALevel()
{
	// Get the direct3d object:
	
	IDirect3D9 * d3d = GetDisplay()->GetDirect3D();

	// Store the result of level checking here:
	
	HRESULT result = D3D_OK;
	
	// Macro to check the levels:
			
	#define CHECK_AA_LEVEL(AA_LEVEL)			\
												\
	result = d3d->CheckDeviceMultiSampleType	\
	(											\
		D3DADAPTER_DEFAULT	,					\
		D3DDEVTYPE_HAL		,					\
		D3DFMT_X8R8G8B8		,					\
		GetDisplay()->IsFullscreen() == false ,	\
		AA_LEVEL ,								\
		0										\
	);

	// Check 2x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_2_SAMPLES); if ( result != D3D_OK ) return Display::ANTIALIAS_NONE;
	
	// Check 4x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_4_SAMPLES); if ( result != D3D_OK ) return Display::ANTIALIAS_2_SAMPLES;
	
	// Check 8x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_8_SAMPLES); if ( result != D3D_OK ) return Display::ANTIALIAS_4_SAMPLES;	

	// Check 16x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_16_SAMPLES); if ( result != D3D_OK ) return Display::ANTIALIAS_8_SAMPLES;	
	
	// Return 16x:
			
	return Display::ANTIALIAS_16_SAMPLES;	
}
