//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================


#include "mdllib_utils.h"


//////////////////////////////////////////////////////////////////////////
//
// CInsertionTracker implementation
//
//////////////////////////////////////////////////////////////////////////


void CInsertionTracker::InsertBytes( void *pos, int length )
{
	if ( length <= 0 )
		return;

	Assert( m_map.InvalidIndex() == m_map.Find( ( byte * ) pos ) );
	m_map.InsertOrReplace( ( byte * ) pos, length );
}

int CInsertionTracker::GetNumBytesInserted() const
{
	int iInserted = 0;

	for ( Map::IndexType_t idx = m_map.FirstInorder();
		idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) )
	{
		int numBytes = m_map.Element( idx );
		iInserted += numBytes;
	}

	return iInserted;
}

void CInsertionTracker::Finalize()
{
	// Iterate the map and find all the adjacent removal data blocks
	// TODO:
}

void CInsertionTracker::MemMove( void *ptrBase, int &length ) const
{
	int numBytesInsertReq = GetNumBytesInserted();
	byte *pbBlockEnd = BYTE_OFF_PTR( ptrBase, length );
	length += numBytesInsertReq;

	for ( Map::IndexType_t idx = m_map.LastInorder();
		idx != m_map.InvalidIndex(); idx = m_map.PrevInorder( idx ) )
	{
		byte *ptr = m_map.Key( idx );
		int numBytes = m_map.Element( idx );

		// Move [ptr, pbBlockEnd) ->> + numBytesInsertReq
		memmove( BYTE_OFF_PTR( ptr, numBytesInsertReq ), ptr, BYTE_DIFF_PTR( ptr, pbBlockEnd ) );

		// Inserted data
		memset( BYTE_OFF_PTR( ptr, numBytesInsertReq - numBytes ), 0, numBytes );

		numBytesInsertReq -= numBytes;
		pbBlockEnd = ptr;
	}
}

int CInsertionTracker::ComputeOffset( void *ptrBase, int off ) const
{
	void *ptrNewBase = ComputePointer( ptrBase );
	void *ptrNewData = ComputePointer( BYTE_OFF_PTR( ptrBase, off ) );
	return BYTE_DIFF_PTR( ptrNewBase, ptrNewData );
}

void * CInsertionTracker::ComputePointer( void *ptrNothingInserted ) const
{
	int iInserted = 0;

	// Iterate the map and find all the data that would be inserted before the given pointer
	for ( Map::IndexType_t idx = m_map.FirstInorder();
		idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) )
	{
		if ( m_map.Key( idx ) < ptrNothingInserted )
			iInserted += m_map.Element( idx );
		else
			break;
	}

	return BYTE_OFF_PTR( ptrNothingInserted, iInserted );
}



//////////////////////////////////////////////////////////////////////////
//
// CRemoveTracker implementation
//
//////////////////////////////////////////////////////////////////////////


void CRemoveTracker::RemoveBytes( void *pos, int length )
{
	if ( length <= 0 )
		return;

	// -- hint
	if ( m_map.Count() )
	{
		if ( m_hint.ptr < pos )
		{
			if ( BYTE_OFF_PTR( m_hint.ptr, m_hint.len ) == pos )
			{
				m_hint.len += length;
				m_map.Element( m_hint.idx ) = m_hint.len;
				return;
			}
		}
		else if ( m_hint.ptr > pos )
		{
			if ( BYTE_OFF_PTR( pos, length ) == m_hint.ptr )
			{
				m_hint.len += length;
				m_hint.ptr = BYTE_OFF_PTR( m_hint.ptr, - length );
				m_map.Key( m_hint.idx ) = m_hint.ptr;
				m_map.Element( m_hint.idx ) = m_hint.len;
				return;
			}
		}
	}
	// -- end hint

	// Insert new
	Assert( m_map.InvalidIndex() == m_map.Find( ( byte * ) pos ) );
	Map::IndexType_t idx = m_map.InsertOrReplace( ( byte * ) pos, length );
	
	// New hint
	m_hint.idx = idx;
	m_hint.ptr = ( byte * ) pos;
	m_hint.len = length;
}

int CRemoveTracker::GetNumBytesRemoved() const
{
	int iRemoved = 0;

	for ( Map::IndexType_t idx = m_map.FirstInorder();
		idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) )
	{
		int numBytes = m_map.Element( idx );
		iRemoved += numBytes;
	}

	return iRemoved;
}

void CRemoveTracker::Finalize()
{
	// Iterate the map and find all the adjacent removal data blocks
	// TODO:
}

void CRemoveTracker::MemMove( void *ptrBase, int &length ) const
{
	int iRemoved = 0;

	for ( Map::IndexType_t idx = m_map.FirstInorder();
		idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) )
	{
		byte *ptr = m_map.Key( idx );
		byte *ptrDest = BYTE_OFF_PTR( ptr, - iRemoved );
		int numBytes = m_map.Element( idx );
		memmove( ptrDest, BYTE_OFF_PTR( ptrDest, numBytes ), BYTE_DIFF_PTR( BYTE_OFF_PTR( ptr, numBytes ), BYTE_OFF_PTR( ptrBase, length ) ) );
		iRemoved += numBytes;
	}

	length -= iRemoved;
}

int CRemoveTracker::ComputeOffset( void *ptrBase, int off ) const
{
	void *ptrNewBase = ComputePointer( ptrBase );
	void *ptrNewData = ComputePointer( BYTE_OFF_PTR( ptrBase, off ) );
	return BYTE_DIFF_PTR( ptrNewBase, ptrNewData );
}

void * CRemoveTracker::ComputePointer( void *ptrNothingRemoved ) const
{
	int iRemoved = 0;

	// Iterate the map and find all the data that would be removed before the given pointer
	for ( Map::IndexType_t idx = m_map.FirstInorder();
		idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) )
	{
		if ( m_map.Key( idx ) < ptrNothingRemoved )
			iRemoved += m_map.Element( idx );
		else
			break;
	}

	return BYTE_OFF_PTR( ptrNothingRemoved, - iRemoved );
}