////////////////////////////////////////////////////////////////////////////////
//
// 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
//
//////////////////////////////////////////////////////////////////////
//
//	CHIP.CPP
//   
//	Created on 1/31/96	JMI 
//
//////////////////////////////////////////////////////////////////////
//
// Container class for a chip's sprite (x, y, z, etc.).
//
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Blue Includes.
//////////////////////////////////////////////////////////////////////
//#include "common/system.h"

//////////////////////////////////////////////////////////////////////
// Green Includes.
//////////////////////////////////////////////////////////////////////
//#include "BLiT/blit2d/blimage.h"
#include "RSPiX.h"

//////////////////////////////////////////////////////////////////////
// Orange Includes.
//////////////////////////////////////////////////////////////////////
//#include "cdt/queue.h"
#include "chip.h"

//////////////////////////////////////////////////////////////////////
// Macros.
//////////////////////////////////////////////////////////////////////
#define MAX_CHIPS	4096

#define BASE		(m_pimChip->lHeight)
#define THICKNESS	0.50F

//////////////////////////////////////////////////////////////////////
// Typedefs.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Protos.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Module specific (static) vars.
//////////////////////////////////////////////////////////////////////
RSList<CChip, float>	CChip::ms_slistChips;	// List of all 
															// chips sorted by
															// each chip's Z
															// position.
RImage*	CChip::ms_pimView		= NULL;	// Background for all chips.
RImage*	CChip::ms_pimStack		= NULL;	// Stack for all chips.

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

//////////////////////////////////////////////////////////////////////
// Con/Destruction.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//
// Default constructor.
// Returns nothing.
//
//////////////////////////////////////////////////////////////////////
CChip::CChip()
	{
	Init();

	if (ms_slistChips.Insert(this, &m_fZ) == 0)
		{
		}
	else
		{
		TRACE("CChip(): Unable to Insert this into list.\n");
		}
	}

//////////////////////////////////////////////////////////////////////
//
// Destructor.
// Returns nothing.
//
//////////////////////////////////////////////////////////////////////
CChip::~CChip()
	{
	Reset();

	if (ms_slistChips.Remove(this) == 0)
		{
		}
	else
		{
		TRACE("CChip(): Unable to Remove this from list.\n");
		}
	}

//////////////////////////////////////////////////////////////////////
// Methods.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//
// Move this chip to the new location, erasing the old.
// Returns nothing.
//
//////////////////////////////////////////////////////////////////////
void CChip::SetPosition(int32_t lX, int32_t lY, int32_t lZ)
	{
	float	fOldZ	= m_fZ;

	m_fX	= (float)lX;
	m_fY	= (float)lY;
	m_fZ	= (float)lZ;

	// If the z position changed . . .
	if (fOldZ != m_fZ)
		{
		// We must reposition this item.
		ms_slistChips.Reposition(this);
		}
	}

//////////////////////////////////////////////////////////////////////
//
// Slide this chip on update to the new location.
// lRate is the magnitude of the vector directing the
// chip.
// You may call this when the chip is already sliding to redirect it.
// Returns 0 if successfully started.
//
//////////////////////////////////////////////////////////////////////
int16_t CChip::Slide(int32_t lX, int32_t lY, int32_t lZ, int32_t lRate)
	{
	int16_t	sRes	= 0;	// Assume success.

	// Store destination position.
	m_sDestX	= (int16_t)lX;
	m_sDestY	= (int16_t)lY;
	m_sDestZ	= (int16_t)lZ;

	m_sRate	= (int16_t)lRate;

	// Success.
	m_sSliding	= TRUE;

	return sRes;
	}

//////////////////////////////////////////////////////////////////////
//
// Reset this chip.  Stop sliding, if doing so.
// Initialize/Release all members.
//
//////////////////////////////////////////////////////////////////////
void CChip::Reset(void)
	{
	Init();
	}

//////////////////////////////////////////////////////////////////////
//
// Update movement.
// Returns 0 normally, 1 if chip destroyed.
//
//////////////////////////////////////////////////////////////////////
int16_t CChip::Update(void)
	{
	int16_t	sDeleted	= 0;	// Assume normal ops.

	if (m_sSliding == TRUE)
		{
		m_fX	+= ((float)m_sDestX - m_fX) / (float)m_sRate;
		m_fY	+= ((float)m_sDestY - m_fY) / (float)m_sRate;
		m_fZ	+= ((float)m_sDestZ - m_fZ) / (float)m_sRate;

		m_sRate--;
		ASSERT(m_sRate >= 0);

		if (	(int16_t)m_fX == m_sDestX
			&&	(int16_t)m_fY	== m_sDestY
			&& (int16_t)m_fZ	== m_sDestZ)
			{
			// Done.
			m_sSliding	= FALSE;
			// Stack if necessary.
			sDeleted	= Stack();
			}
		}

	return sDeleted;
	}


//////////////////////////////////////////////////////////////////////
//
// Called to draw this chip.
//
//////////////////////////////////////////////////////////////////////
void CChip::Draw(void)
	{
	ASSERT(ms_pimView != NULL);
	ASSERT(m_pimChip != NULL);
	
	// If this is merely a chip . . .
	if (m_sNumChips == 1)
		{
		// Put'er there!  NOTE: ItoBKD is a real time macro containing 
		// merely 2 dereferences, worst case.
		rspBlit(m_pimChip, ms_pimView, m_fX, m_fY);
//		BLT_FItoBKD(m_pimChip, ms_pimView, (short)m_fX, (short)m_fY);
		}
	else
		{
		// Otherwise, display a stack.
		int16_t	sHeight	= (int16_t)(THICKNESS * (float)GetSize());
		
		// The smallest stack should look like a stack.
		if (sHeight < 1)
			{
			sHeight = 1;
			}

		for (int16_t	s = 0; s < sHeight; s++)
			{
//			BLT_FItoBKD(m_pimChip, ms_pimView, (short)m_fX, (short)m_fY - s);
			rspBlit(m_pimChip, ms_pimView, m_fX, m_fY - s);
			}
		}
	}

//////////////////////////////////////////////////////////////////////
//
// Called to update all sliding chips.
// Technically, we should go through the list twice.  Once to move the
// chips and rearrange them in the list and once to blt them.
// We cannot rearrange them as we go through the list or we could end
// up processing a chip twice or not at all.  This indicates going
// through the list three times:
// 1) Move chips and set a flag to rearrange.
// 2) Rearrange.
// 3) Blt.
// OR
// we could go through the list once adding all the ones to be re-
// arranged to a queue.  Reprocess only those, rearranging as 
// necessary and then traverse the list and blt:
// 1) Move chips and add those with new Z positions to a queue.
// 2) Empty queue rearranging queued chips only.
// 3) Blt chips.
//
//////////////////////////////////////////////////////////////////////
void CChip::Critical(void)
	{
	RQueue<CChip, MAX_CHIPS>	qChips;

	// Step 1: Traverse the Z sorted list updating all chips and 
	// adding those that change their Z position to a queue of chips
	// to be repositioned.
	CChip*	pchip	= ms_slistChips.GetHead();
	float		fOldZ;
	while (pchip != NULL)
		{
		fOldZ	= pchip->m_fZ;

		// If not deleted during update . . .
		if (pchip->Update() == 0)
			{
			// If this chip changed its Z position . . .
			if (fOldZ != pchip->m_fZ)
				{
				// Add it to the queue of chips to be resorted.
				qChips.EnQ(pchip);
				}
			}

		// Get the next chip to move.
		pchip	= ms_slistChips.GetNext();
		}

	// Step 2: Empty the queue resorting each chip.
	pchip	= qChips.DeQ();
	while (pchip != NULL)
		{
		// Reposition this chip in the sorted list since its Z position
		// changed.
		ms_slistChips.Reposition(pchip);

		pchip = qChips.DeQ();
		}

	// Step 3: Blt the chips in order.
	pchip	= ms_slistChips.GetHead();
	while (pchip != NULL)
		{
		pchip->Draw();

		pchip = ms_slistChips.GetNext();
		}

	}

//////////////////////////////////////////////////////////////////////
//
// Destroy all current chips.
//
//////////////////////////////////////////////////////////////////////
void CChip::DeleteAll(void)
	{
	CChip*	pchip	= ms_slistChips.GetHead();

	while (pchip != NULL)
		{
		// Someday a chip may have data to allocate.
		pchip->Reset();

		// Destructor pulls off of list.
		delete pchip;

		pchip	= ms_slistChips.GetNext();
		}
	}

//////////////////////////////////////////////////////////////////////
//
// Stack chip if necessary. 
// Returns 0 normally, 1 if chip destroyed.
//
//////////////////////////////////////////////////////////////////////
int16_t CChip::Stack(void)
	{
	int16_t	sDeleted	= 0;	// Assume normal ops.

	if (m_sStackable != FALSE)
		{
		// If we are near a chip that's not sliding . . .
		CChip* pchip	= IsColliding(FALSE);
		if (pchip != NULL)
			{
			// Add this chip/stack to that stack.
			pchip->Add(this);

			sDeleted	= TRUE;

			// It's important that we delete this chip and
			// add to the other as, if we are in one of
			// the Critical loops, Remove()ing another chip
			// from the list of chips will make that chip
			// the current and screw up the loop's next call
			// to GetNext().
			// Destroy this chip (it is reflected in the new
			// size of pchip's stack).
			delete this;
			}
		}

	return sDeleted;
	}

//////////////////////////////////////////////////////////////////////
//
// Add to this stack.
// Returns 0 on success.
//
//////////////////////////////////////////////////////////////////////
int16_t CChip::Add(CChip*	pchip)
	{
	int16_t	sRes	= 0;	// Assume success.

	// Take stack's size and add to this one's.
	m_sNumChips += pchip->GetSize();

	return sRes;
	}

//////////////////////////////////////////////////////////////////////
//
// Remove sNum chips from this stack.
// Returns chip/stack on success; NULL on error.
//
//////////////////////////////////////////////////////////////////////
CChip* CChip::Sub(int16_t sNum)
	{
	CChip*	pchip	= NULL;	// Assume failure.

	// If we have sNum to give up . . .
	if (sNum < GetSize())
		{
		// Create a new chip/stack for these.
		pchip	= new CChip;
		// Make it empty for now.
		pchip->SetSize(0);
		}
	else
		{
		if (sNum == GetSize())
			{
			// Use this chip.
			pchip	= this;
			}
		}

	// If successful . . . 
	if (pchip != NULL)
		{
		// Add to new stack.
		pchip->SetSize(pchip->GetSize() + sNum);
		// Remove from this.
		SetSize(GetSize() - sNum);

		// Some attributes that should be copied.
		pchip->SetPosition((int32_t)m_fX, (int32_t)m_fY, (int32_t)m_fZ);
		pchip->SetChipImage(GetChipImage());
		}

	return pchip;
	}

//////////////////////////////////////////////////////////////////////
// Querries.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//
// Returns the first chip found to be colliding
// with this chip.  If sTop is TRUE, the highest,
// the one with the greatest Y, is found.
//
//////////////////////////////////////////////////////////////////////
CChip* CChip::IsColliding(int16_t sTop/*	= FALSE*/)
	{
	CChip*	pchip	= NULL;				// Assume not found.
	CChip*	pchipHighest	= NULL;	// If sTop is TRUE, this is used
												// to store the chip with the
												// lowest Y.

	for (int16_t i = 0; i < 2 && (pchip == NULL || sTop == TRUE); i++)
		{
		// Search back from this one . . .
		pchip	= (i == 0	? ms_slistChips.GetPrev(this) 
								: ms_slistChips.GetNext(this));

		while (pchip != NULL && pchip != this)
			{
			// Is it outside the Z range?
			// NOTE: If this check stops the search, it can screw up in the
			// event that another chip has a different size.
			if (	pchip->m_fZ + (float)pchip->m_pimChip->m_sHeight < m_fZ
				||	pchip->m_fZ > m_fZ + (float)m_pimChip->m_sHeight)
				{
				// Outside.
				pchip	= NULL;
				}
			else
				{
				// Is it outside the X range?
				// NOTE: this check has the same limitation as above.
				if (	pchip->m_fX + pchip->m_pimChip->m_sWidth < m_fX
					||	pchip->m_fX > m_fX + (float)m_pimChip->m_sWidth)
					{
					// Outside.
					}
				else
					{
					if (pchip->m_sStackable != FALSE)
						{
						// If not looking for the highest . . .
						if (sTop == FALSE)
							{
							// We're done.
							break;
							}
						else
							{
							if (pchipHighest == NULL)
								{
								pchipHighest	= pchip;
								}
							else
								{
								if (pchip->m_fY < pchipHighest->m_fY)
									{
									pchipHighest	= pchip;
									}
								}
							}
						}
					else
						{
						// Can't use a stackable chip.
						}
					}

				pchip = (i == 0	? ms_slistChips.GetPrev()
										: ms_slistChips.GetNext() );
				}
			}
		}


	// If we're searching for the highest and we found one . . .
	if (sTop == TRUE && pchipHighest != NULL)
		{
		pchip = pchipHighest;
		}

	return pchip;
	}

//////////////////////////////////////////////////////////////////////
//
// Returns the first chip/stack in the given rectangle.
// Returns NULL if none found.
// (static)
//
//////////////////////////////////////////////////////////////////////
CChip* CChip::GetChipIn(int32_t lX, int32_t lY, int32_t lW, int32_t lH)
	{
	CChip*	pchip	= ms_slistChips.GetHead();
	while (pchip != NULL)
		{
		if (	(int32_t)pchip->m_fX < lX 
			||	(int32_t)pchip->m_fX > lX + lW
			|| (int32_t)pchip->m_fY < lY
			||	(int32_t)pchip->m_fY > lY + lH)
			{
			// No match.
			pchip = ms_slistChips.GetNext();
			}
		else
			{
			// Found one.
			break;
			}
		}

	return pchip;
	}

//////////////////////////////////////////////////////////////////////
// Internal.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//
// Initialize all members regardless of their current values.
//
//////////////////////////////////////////////////////////////////////
void CChip::Init(void)
	{
	m_fX		= 0.0F;
	m_fY		= 0.0F;
	m_fZ		= 0.0F;

	m_sDestX	= 0;
	m_sDestY	= 0;
	m_sDestZ	= 0;

	m_sRate	= 0;

	m_pimChip	= NULL;

	m_sSliding	= FALSE;

	m_sStackable	= TRUE;

	m_sNumChips		= 1;
	}

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