/*
	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 vertex buffer that can be used to store geometric data on the graphics card 
///				(or wherever the driver places it). Vertex buffers can be either static or dynamic. See the notes
///				concernng the vertex buffer type.
//=====================================================================================================================

#include "Precompiled.h"
#include "Graphics\VertexBuffer.h"
#include "Graphics\Display.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates the vertex buffer
/// @param		display - display the vertex buffer belongs to
/// @param		type - what type of vertex buffer this is
/// @param		size - size of the vertex buffer
/// @pre		Size must be at least 1 byte
/// @remarks	Static vertex buffers will have their contents marked as being lost until the first
///				attempt to fill them is made.
//=====================================================================================================================

VertexBuffer::VertexBuffer( Display * display , Type type , unsigned size ) : DisplayEvictable(display)
{
	// Make sure ok

	ENSURE( size >= 1 , "Size must be at least 1 byte" )

	// Initialise all fields:

	m_vb				= 0;
	m_lost_contents		= true;
	m_vb_mem_copy		= 0;
	m_size				= size;
	m_type				= type;

	// Create the vertex buffer

	RecreateEvictableResources();
}

//=====================================================================================================================
/// @brief		Destroys the vertex buffer
//=====================================================================================================================

VertexBuffer::~VertexBuffer()
{
	// Release our memory copy of the vertex buffer if we have one:

	if ( m_vb_mem_copy )
	{
		// Delete the memory

		delete[] ((__int8*)(m_vb_mem_copy)); m_vb_mem_copy = 0;
	}

	// Release all of our resources
	
	ReleaseEvictableResources();
}

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

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

//=====================================================================================================================
/// @brief		Tells the resource to release all it's resources upon the graphics device being lost: 
///				only the display should call this! 
/// @pre		This should not be called if the resource is invalid
//=====================================================================================================================

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

	ENSURE( IsValid() , "This should not be called if the resource is invalid" )
	
	// Release our vertex buffer:

	m_vb->Release(); m_vb = 0; m_is_valid = false;

	// If this is not a static VB we have now lost our contents

	if ( m_type != TYPE_STATIC ) m_lost_contents = true;
}

//=====================================================================================================================
/// @brief		Tells the resource to recreate all it's resources upon the graphics device being recreated: 
///				only the display should call this! 
/// @pre		The vertex buffer should be associated with a valid display
/// @pre		The vertex buffer should have been released beforehand
//=====================================================================================================================

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

	ENSURE( GetDisplay()	, "The vertex buffer should be associated with a valid display" )
	ENSURE( m_vb == 0		, "The vertex buffer should have been released beforehand"		)

	// Decide on the usage flags for the vertex buffer

	DWORD usage = 0;
	
	// Decide:

	switch ( m_type )
	{
		// static vertex buffer

		case TYPE_STATIC:

			usage |= D3DUSAGE_WRITEONLY;

		break;

		// dynamic vertex buffer

		case TYPE_DYNAMIC:

			usage |= D3DUSAGE_DYNAMIC;
			usage |= D3DUSAGE_WRITEONLY;

		break;
	}

	// Make the vertex buffer:

	HRESULT result = GetDisplay()->GetDevice()->CreateVertexBuffer
	(
		m_size			,
		usage			,
		0				,		// fixed function format not used
		D3DPOOL_DEFAULT	,		// we manage this resource across device resets
		&m_vb			,
		0						// vista only d3d shared handle
	);

	// See if that succeeded:

	if ( result == D3D_OK )
	{
		// Now valid
		
		m_is_valid = true;
	
		// Good stuff: see if we have a memory copy of the buffer:

		if ( m_vb_mem_copy )
		{
			// Need to recopy in the buffer contents for the static buffer

			void * locked_mem = 0; 

				// Do the lock:
			
				result = m_vb->Lock
				(
					0					,	// offset to lock
					m_size				,	// size to lock
					&locked_mem			,	// where to save pointer
					D3DLOCK_NOOVERWRITE		// don't wait for anything to finish using the buffer
				);

				// See if that was ok:

				if ( locked_mem && result == D3D_OK )
				{
					// Copy our memory into the buffer:

					memcpy(locked_mem,m_vb_mem_copy,m_size);
				}
					
				// Unlock:

				m_vb->Unlock();
		}
	}
	else
	{
		// No longer valid:
		
		m_is_valid = false;
	
		// If we have a vertex buffer then release it:

		if ( m_vb ) m_vb->Release();
	}
}

//=====================================================================================================================
/// @brief		Writes to the specified area of the vertex buffer
/// @param		data - where to copy data from into the vertex buffer
/// @param		buffer_offset - offset in the buffer to write to
/// @param		size - amount of data to write
/// @param		wait_till_free - if true the vertex buffer will wait until all
///				objects drawing from the buffer are finished with it. This may
///				cause stalls in some cases where data is being appended to a 
///				buffer in use. If you know that the area being written to is not
///				currently in use by a draw call, then this flag can be set to
///				false for increased performance and parralellization with the GPU.
/// @pre		This vertex buffer must be valid
///	@pre		The given data buffer must not be null
/// @pre		The amount of data to write must not be zero
/// @pre		The write operation must not overflow the buffer
/// @remarks	If for some reason the write operation fails then the failure will be silent.
///				The lost contents flag will be set to false in this case, but for static vertex
///				buffers however the contents of the vertex buffer will still be backed up in memory
///				in case the write failed due to a device reset.
/// @remarks	If this is the first write using either a dynamic or static VB and the write area
///				does not cover the entire buffer, then the rest of the buffer contents will be undefined
///				following the write operation.
/// @remarks	Calling this function will automatically set the lost contents flag to false. If the 
///				buffer is only partially updated after a content loss, the user of this class will not
///				be informed again about the lost contents of the buffer.
//=====================================================================================================================

void VertexBuffer::Write( void * data , unsigned buffer_offset , unsigned size , bool wait_till_free )
{
	// Make sure ok

	ENSURE( IsValid()						, "This vertex buffer must be valid"					)
	ENSURE( data							, "The given data buffer must not be null"				)
	ENSURE( size > 0						, "The amount of data to write must not be zero"		)
	ENSURE( buffer_offset + size <= m_size	, "The write operation must not overflow the buffer"	)

	// Lock the buffer

	void * locked_mem = 0;

	// Makeup the lock flags:

	DWORD lock_flags = 0;

	// See if we want to wait for the gpu to finish using the buffer:

	if ( wait_till_free ) lock_flags |= D3DLOCK_NOOVERWRITE;

	// If we are writing to the entire buffer we can safely discard all contents if required:

	if ( size == m_size )
	{
		lock_flags |= D3DLOCK_DISCARD;
	}

	// Do the lock:

	HRESULT result = m_vb->Lock
	(
		buffer_offset		,	// offset to lock
		m_size				,	// size to lock
		&locked_mem			,	// where to save pointer
		lock_flags				// flags when locking
	);

	// See if that was ok:

	if ( locked_mem && result == D3D_OK )
	{
		// Copy the given memory into the vertex buffer

		memcpy( locked_mem , data , size );
	}

	// See if we are using static vertex buffers:

	if ( m_type == TYPE_STATIC )
	{
		// If we do not have our own copy of the vertex buffer then make one

		if ( m_vb_mem_copy == 0 )
		{
			// Make it:

			m_vb_mem_copy = (void*)( new __int8[m_size] );
		}

		// Copy the data written into our memory copy of the buffer:

		memcpy( (void*)(((unsigned)(m_vb_mem_copy)) + buffer_offset ) , data , size );
	}

	// Unlock:

	m_vb->Unlock();

	// No longer lost the contents of the vertex buffer

	m_lost_contents = false;
}
