////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// IdBank.cpp
// 
// History:
//		01/29/97 JMI	Started.
//
//		01/30/97	JMI	Removed the bug I put in Add() that access the Head of
//							the list to add to, whether or not it was a valid node.
//
//		01/30/97	JMI	Fixed bug in Remove().  In rush for demo.
//
//		02/24/97	JMI	Changed Add() to Insert() and created new Add() that
//							adds at the end.
//							Also, removed m_u16HeadUsedId.  It wasn't useful.
//
//		03/05/97	JMI	GetThingByID() now fails for IdNil without complaint.
//
//////////////////////////////////////////////////////////////////////////////
//
// See .H for details.
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// C Headers -- Must be included before RSPiX.h b/c RSPiX utilizes SHMalloc.
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// RSPiX Headers.
// If PATHS_IN_INCLUDES macro is defined, we can utilize relative
// paths to a header file.  In this case we generally go off of our
// RSPiX root directory.  System.h MUST be included before this macro
// is evaluated.  System.h is the header that, based on the current
// platform (or more so in this case on the compiler), defines 
// PATHS_IN_INCLUDES.  Blue.h includes system.h so you can include that
// instead.
///////////////////////////////////////////////////////////////////////////////
#include "RSPiX.h"

#ifdef PATHS_IN_INCLUDES

#else

#endif

//////////////////////////////////////////////////////////////////////////////
// Postal includes.
//////////////////////////////////////////////////////////////////////////////

#include "IdBank.h"

//////////////////////////////////////////////////////////////////////////////
// Module specific macros.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Module specific typedefs.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Exported (extern) variables.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Module specific (static) variables / Instantiate class statics.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Module specific (static) protos.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Functions.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Helper to insert an ID into a particular list.
//
//////////////////////////////////////////////////////////////////////////////
void CIdBank::Insert(	// Returns nothing.
	U16	u16Id,			// ID to insert.
	U16*	pu16IdHead)		// Head of list to add to.
	{
	// Point this node's next at the current head of the free list.
	m_aids[u16Id].u16IdNext			= *pu16IdHead;
	// If there is a head . . .
	if (*pu16IdHead != IdNil)
		{
		// Point current head node's prev at this node.
		m_aids[*pu16IdHead].u16IdPrev	= u16Id;
		}

	// Don't look back.
	m_aids[u16Id].u16IdPrev	= IdNil;

	// Make this node the new head of the list.
	*pu16IdHead								= u16Id;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Helper to add an ID to a particular list.
//
//////////////////////////////////////////////////////////////////////////////
void CIdBank::Add(	// Returns nothing.
	U16	u16Id,		// ID to add.
	U16*	pu16IdTail)	// Tail of list to add to.
	{
	// Point this node's prev at the current tail of the free list.
	m_aids[u16Id].u16IdPrev			= *pu16IdTail;
	// If there is a head . . .
	if (*pu16IdTail != IdNil)
		{
		// Point current tail node's next at this node.
		m_aids[*pu16IdTail].u16IdNext	= u16Id;
		}

	// Don't look forward.
	m_aids[u16Id].u16IdNext	= IdNil;

	// Make this node the new tail of the list.
	*pu16IdTail								= u16Id;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Helper to remove an ID from a particular list.
//
//////////////////////////////////////////////////////////////////////////////
void CIdBank::Remove(	// Returns nothing.
	U16	u16Id,			// ID to remove.
	U16*	pu16IdHead,		// Head of list to remove from.
	U16*	pu16IdTail)		// Tail of list to remove from.
	{
	// If this was the head . . .
	if (*pu16IdHead == u16Id)
		{
		// Make the head this node's next.
		*pu16IdHead	= m_aids[u16Id].u16IdNext;
		// If not the end . . .
		if (*pu16IdHead != IdNil)
			{
			// Make the new head's prev NIL.
			m_aids[*pu16IdHead].u16IdPrev	= IdNil;
			}
		}
	else
		{
		// Set prev's next to this ID's next.
		m_aids[m_aids[u16Id].u16IdPrev].u16IdNext	= m_aids[u16Id].u16IdNext;
		}

	// If this was the tail . . .
	if (*pu16IdTail == u16Id)
		{
		// Make the tail this node's prev.
		*pu16IdTail	= m_aids[u16Id].u16IdPrev;
		// If not the beginning . . .
		if (*pu16IdTail != IdNil)
			{
			// Make the new tail's next NIL.
			m_aids[*pu16IdTail].u16IdNext	= IdNil;
			}
		}
	else
		{
		// Set next's prev to this ID's prev.
		m_aids[m_aids[u16Id].u16IdNext].u16IdPrev	= m_aids[u16Id].u16IdPrev;
		}
	}

//////////////////////////////////////////////////////////////////////////////
//
// Resets all IDs to free.  This must be called before the first use of
// any of the rest of these functions.
//
//////////////////////////////////////////////////////////////////////////////
void CIdBank::Reset(void)
	{
	// Reset all IDs.
	// Initialize all IDs regardless of current contents.
	U16	u16Cur;
	U16	u16Prev;
	for (u16Cur	= 0, u16Prev = IdNil; u16Cur < NumIds; u16Cur++, u16Prev++)
		{
		m_aids[u16Cur].u16IdPrev	= u16Prev;
		m_aids[u16Cur].u16IdNext	= u16Cur + 1;
		m_aids[u16Cur].pthing		= NULL;
		}

	// Last item's next should indicate end.
	m_aids[u16Prev].u16IdNext		= IdNil;

	// Start at beginning for Free List head.
	m_u16HeadFreeId	= 0;

	// Start at end for Free List tail.
	m_u16TailFreeId	= NumIds - 1;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Get a unique ID and associate it with a thing (CThing, that is).
//
//////////////////////////////////////////////////////////////////////////////
int16_t CIdBank::Get(	// Returns 0 on success.
	CThing*	pthing,	// In:  Thing that wants to get an ID and be put in
							// the ID table.
	U16*		pu16ID)	// Out: ID for this particular CThing.
	{
	int16_t	sRes	= 0;	// Assume success.

	// Make sure there's one left . . .
	if (m_u16HeadFreeId != IdNil)
		{
		// Get ID.
		*pu16ID	= m_u16HeadFreeId;

		// Set IDs value.
		m_aids[*pu16ID].pthing		= pthing;

		// Remove from free list and get next head.
		m_u16HeadFreeId	= m_aids[*pu16ID].u16IdNext;
		}
	else
		{
		TRACE("GetUniqueID(): Out of IDs!\n");
		sRes	= -1;
		}

	return sRes;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Take a unique ID and associate it with a thing (CThing).
//
//////////////////////////////////////////////////////////////////////////////
int16_t CIdBank::Take(	// Returns 0 on success.
	CThing*	pthing,	// In:  Thing that wants to take an ID and be put in
							// the ID table.
	U16		u16ID)	// In:  ID for this particular CThing.
	{
	int16_t	sRes	= 0;	// Assume success.

	// Range check.
	ASSERT(u16ID < NumIds);

	// Make sure the ID is available . . .
	if (m_aids[u16ID].pthing == NULL)
		{
		// Set IDs value.
		m_aids[u16ID].pthing		= pthing;

		// Remove from free list.
		Remove(u16ID, &m_u16HeadFreeId, &m_u16TailFreeId);
		}
	else
		{
		TRACE("TakeUniqueID(): ID not available!\n");
		sRes	= -1;
		}

	return sRes;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Release ID and unregister thing associated with it.
//
//////////////////////////////////////////////////////////////////////////////
void CIdBank::Release(	// Returns nothing.
	U16		u16ID)		// ID to release.
	{
	// If a valid ID . . .
	if (u16ID != IdNil)
		{
		// Range check.
		ASSERT(u16ID < NumIds);
		// The ID should be in use.  If not, something has hosened.
		ASSERT(m_aids[u16ID].pthing != NULL);

		// Clear ID.
		m_aids[u16ID].pthing		= NULL;

		// Add to free list.
		Add(u16ID, &m_u16TailFreeId);
		}
	}

//////////////////////////////////////////////////////////////////////////////
//
// Get a CThing via its ID.
//
//////////////////////////////////////////////////////////////////////////////
int16_t CIdBank::GetThingByID(	// Returns 0 on success.
	CThing**	ppthing,				// Out: Ptr to CThing identified by u16ID.
	U16		u16ID)				// In:  ID of thing to get.
	{
	int16_t	sRes	= 0;	// Assume success.

	if (u16ID != IdNil)
		{
		// Range check.
		ASSERT(u16ID < NumIds);

		// Get thing.
		*ppthing	= m_aids[u16ID].pthing;
		
		// This ID should be used.
		if (*ppthing != NULL)
			{
			// Success.
			}
		else
			{
//			TRACE("GetThingByID(): No such ID.\n");
			sRes	= -2;
			}
		}
	else
		{
		*ppthing	= NULL;
		sRes	= -1;
		}

	return sRes;
	}

///////////////////////////////////////////////////////////////////////////////
// EOF
///////////////////////////////////////////////////////////////////////////////
