
#include "3dc.h"
#include "module.h"

#include "stratdef.h"
#include "sfx.h"

#define UseLocalAssert Yes
#include "ourasert.h"


/* globals for export */

int NumActiveBlocks;
DISPLAYBLOCK *ActiveBlockList[maxobjects];




/*
 Object Block Lists et al
*/

static int NumFreeBlocks;
static DISPLAYBLOCK *FreeBlockList[maxobjects];
static DISPLAYBLOCK **FreeBlockListPtr = &FreeBlockList[maxobjects-1];
static DISPLAYBLOCK FreeBlockData[maxobjects];
static DISPLAYBLOCK **ActiveBlockListPtr = &ActiveBlockList[0];


/*
 Texture Animation Block Extensions
*/

static int NumFreeTxAnimBlocks;
static TXACTRLBLK *FreeTxAnimBlockList[maxTxAnimblocks];
static TXACTRLBLK **FreeTxAnimBlockListPtr = &FreeTxAnimBlockList[maxTxAnimblocks-1];
static TXACTRLBLK FreeTxAnimBlockData[maxTxAnimblocks];



/*
 Light Block Extensions
*/

static int NumFreeLightBlocks;
static LIGHTBLOCK *FreeLightBlockList[maxlightblocks];
static LIGHTBLOCK **FreeLightBlockListPtr = &FreeLightBlockList[maxlightblocks-1];
static LIGHTBLOCK FreeLightBlockData[maxlightblocks];



/*

 To create the free block list, pointers to "FreeBlockData[]" must be copied
 to "FreeBlockList[]".

 Also:

 "NumFreeBlocks" must be updated as "FreeBlockList[]" is created.
 "NumActiveBlocks" must be initialised to zero.

*/
void InitialiseObjectBlocks(void)
{

	DISPLAYBLOCK *FreeBlkPtr = &FreeBlockData[0];

	NumActiveBlocks = 0;

	FreeBlockListPtr   = &FreeBlockList[maxobjects-1];
	ActiveBlockListPtr = &ActiveBlockList[0];

	for(NumFreeBlocks = 0; NumFreeBlocks<maxobjects; NumFreeBlocks++) {

		FreeBlockList[NumFreeBlocks] = FreeBlkPtr;

		FreeBlkPtr++;

	}
}


/*

 "AllocateObjectBlock()" is identical to the routine "GetBlock"

*/

DISPLAYBLOCK* AllocateObjectBlock(void)
{

	DISPLAYBLOCK *FreeBlkPtr = 0;		/* Default to null ptr */
	int *sptr;
	int i;


	if(NumFreeBlocks) {

		FreeBlkPtr = *FreeBlockListPtr--;

		NumFreeBlocks--;					/* One less free block */

		/* Clear the block */

		sptr = (int *)FreeBlkPtr;
		for(i = sizeof(DISPLAYBLOCK)/4; i!=0; i--)
			*sptr++ = 0;
	}

	return(FreeBlkPtr);
}


/*

 "DeallocateObjectBlock()" is identical to the routine "ReturnBlock"

*/

void DeallocateObjectBlock(DISPLAYBLOCK *dblockptr)
{
	/* Deallocate the Display Block */

	FreeBlockListPtr++;
	*FreeBlockListPtr = dblockptr;

	NumFreeBlocks++;						/* One more free block */
}


/*
 "CreateActiveObject()" calls "AllocateObjectBlock()". An active object is
 passed into the view and strategy routines unless flagged otherwise

 WARNING!

 An active object must ALWAYS be deallocated by "DestroyActiveObject()".
*/

DISPLAYBLOCK* CreateActiveObject(void)
{

	DISPLAYBLOCK *dblockptr;


	dblockptr = AllocateObjectBlock();

	if(dblockptr) {

		*ActiveBlockListPtr++ = dblockptr;

		NumActiveBlocks++;


	}

	return dblockptr;
}


/*

 DestroyActiveObject()

 Remove the block from "ActiveBlockList".
 Use the array model because it's clearer.

 This function returns 0 if successful, -1 if not

*/


int DestroyActiveObject(DISPLAYBLOCK *dblockptr)
{
	int i, light;
	TXACTRLBLK *taptr;

	/* If the block ptr is OK, search the Active Blocks List */
	if(dblockptr) {

		for(i = 0; i < NumActiveBlocks; i++) {

			if(ActiveBlockList[i] == dblockptr) {

				ActiveBlockList[i] = ActiveBlockList[NumActiveBlocks-1];
				NumActiveBlocks--;
				ActiveBlockListPtr--;

				DestroyActiveVDB(dblockptr->ObVDBPtr);	/* Checks for null */

				if(dblockptr->ObNumLights) {
					for(light = dblockptr->ObNumLights - 1; light != -1; light--)
						DeleteLightBlock(dblockptr->ObLights[light], dblockptr);
				}

				/* If no SB, deallocate any Texture Animation Blocks */

				if(dblockptr->ObStrategyBlock == 0) {

					if(dblockptr->ObTxAnimCtrlBlks) {

						taptr = dblockptr->ObTxAnimCtrlBlks;

						while(taptr) {

							DeallocateTxAnimBlock(taptr);

							taptr = taptr->tac_next;

						}

					}

				}


				/* Deallocate the Lazy Morphed Points Array Pointer */

				#if (SupportMorphing && LazyEvaluationForMorphing)
				if(dblockptr->ObMorphedPts) {
					DeallocateMem(dblockptr->ObMorphedPts);
					dblockptr->ObMorphedPts = 0;
				}
				#endif

				/* KJL 16:52:43 06/01/98 - dealloc sfx block if one exists */
				if(dblockptr->SfxPtr)
				{
					DeallocateSfxBlock(dblockptr->SfxPtr);
				}

				DeallocateObjectBlock(dblockptr);		/* Back to Free List */

				/* If this is the current landscape, clear the pointer */

				return 0;
			}
		}
	}

	return -1;
}




/*

 Support Functions for Texture Animation Blocks

*/

void InitialiseTxAnimBlocks(void)

{

	TXACTRLBLK *FreeBlkPtr = &FreeTxAnimBlockData[0];


	FreeTxAnimBlockListPtr = &FreeTxAnimBlockList[maxTxAnimblocks-1];

	for(NumFreeTxAnimBlocks=0; NumFreeTxAnimBlocks < maxTxAnimblocks; NumFreeTxAnimBlocks++) {

		FreeTxAnimBlockList[NumFreeTxAnimBlocks] = FreeBlkPtr;

		FreeBlkPtr++;

	}

}


/*

 Allocate a Texture Animation Block

*/

TXACTRLBLK* AllocateTxAnimBlock(void)

{

	TXACTRLBLK *FreeBlkPtr = 0;		/* Default to null ptr */
	int *sptr;
	int i;


	if(NumFreeTxAnimBlocks) {

		FreeBlkPtr = *FreeTxAnimBlockListPtr--;

		NumFreeTxAnimBlocks--;					/* One less free block */

		/* Clear the block */

		sptr = (int *)FreeBlkPtr;
		for(i = sizeof(TXACTRLBLK)/4; i!=0; i--)
			*sptr++ = 0;

	}

	return FreeBlkPtr;

}


/*

 Deallocate a Texture Animation Block

*/

void DeallocateTxAnimBlock(TXACTRLBLK *TxAnimblockptr)

{

	FreeTxAnimBlockListPtr++;

	*FreeTxAnimBlockListPtr = TxAnimblockptr;

	NumFreeTxAnimBlocks++;						/* One more free block */

}


/*

 Add a Texture Animation Block to a Display Block

*/

void AddTxAnimBlock(DISPLAYBLOCK *dptr, TXACTRLBLK *taptr)

{

	TXACTRLBLK *taptr_tmp;


	if(dptr->ObTxAnimCtrlBlks) {

		taptr_tmp = dptr->ObTxAnimCtrlBlks;

		while(taptr_tmp->tac_next)
			taptr_tmp = taptr_tmp->tac_next;

		taptr_tmp->tac_next = taptr;

	}

	else dptr->ObTxAnimCtrlBlks = taptr;

}





/*

 Support functions for Light Blocks

*/

void InitialiseLightBlocks(void)

{

	LIGHTBLOCK *FreeBlkPtr = &FreeLightBlockData[0];


	FreeLightBlockListPtr = &FreeLightBlockList[maxlightblocks-1];

	for(NumFreeLightBlocks=0; NumFreeLightBlocks < maxlightblocks; NumFreeLightBlocks++) {

		FreeLightBlockList[NumFreeLightBlocks] = FreeBlkPtr;

		FreeBlkPtr++;

	}

}


LIGHTBLOCK* AllocateLightBlock(void)

{

	LIGHTBLOCK *FreeBlkPtr = 0;		/* Default to null ptr */
	int *lptr;
	int i;


	if(NumFreeLightBlocks) {

		FreeBlkPtr = *FreeLightBlockListPtr--;

		NumFreeLightBlocks--;					/* One less free block */

		/* Clear the block */

		lptr = (int *)FreeBlkPtr;
		for(i = sizeof(LIGHTBLOCK)/4; i!=0; i--)
			*lptr++ = 0;

	}

	return(FreeBlkPtr);

}


void DeallocateLightBlock(LIGHTBLOCK *lptr)

{

	/* Not all lights come from the free light list */

	if(lptr->LightFlags & LFlag_WasNotAllocated) return;


	/* Make sure that this light IS from the free light list */

	GLOBALASSERT(
		(lptr >= FreeLightBlockData) &&
		(lptr < &FreeLightBlockData[maxlightblocks])
	);


	/* Ok to return the light */

	FreeLightBlockListPtr++;

	*FreeLightBlockListPtr = lptr;

	NumFreeLightBlocks++;						/* One more free block */

}


/*

 See if there are any free slots in the dptr light block array.
 If there are, allocate a light block, place it in the list and return
 the pointer to the caller.

 A late addition is the passing of a light block (from somewhere, it does
 not matter where). This light block is then added rather than one being
 allocated from the free light block list.

*/

LIGHTBLOCK* AddLightBlock(DISPLAYBLOCK *dptr, LIGHTBLOCK *lptr_to_add)

{

	LIGHTBLOCK **larrayptr;
	LIGHTBLOCK **freelarrayptr;
	LIGHTBLOCK *lptr = 0;
	int i, lfree;


	/* Are there any free slots? */

	lfree = No;

	larrayptr = &dptr->ObLights[0];

	for(i = MaxObjectLights; i!=0 && lfree == No; i--) {

		if(*larrayptr == 0) {

			freelarrayptr = larrayptr;
			lfree = Yes;

		}

		larrayptr++;

	}

	if(lfree) {

		if(lptr_to_add) {

			lptr = lptr_to_add;

		}

		else {

			lptr = AllocateLightBlock();

		}

		if(lptr)
		{
			*freelarrayptr = lptr;
			dptr->ObNumLights++;
		}

	}

	return lptr;

}


/*

 To delete a light block, copy the end block to the new free slot and
 reduce the count by one. Make sure the end block array entry is cleared.

*/

void DeleteLightBlock(LIGHTBLOCK *lptr, DISPLAYBLOCK *dptr)
{

	int i, larrayi;

	DeallocateLightBlock(lptr);

	/* What is lptr's array index? */

	larrayi = -1;							/* null value */

	for(i = 0; i < dptr->ObNumLights; i++)
		if(dptr->ObLights[i] == lptr) larrayi = i;



	/* Proceed only if lptr has been found in the array */

	if(larrayi != -1) {

		/* Copy the end block to that of lptr */

		dptr->ObLights[larrayi] = dptr->ObLights[dptr->ObNumLights - 1];

		/* Clear the end block array entry */

		dptr->ObLights[dptr->ObNumLights - 1] = 0;

		/* One less light in the dptr list */

		dptr->ObNumLights--;

	}
}




/*

 When running the parallel strategies, display and light block deallocation
 must only be done at the end of the frame, AFTER processor synchronisation
 and BEFORE the shadow copy.

*/

int DisplayAndLightBlockDeallocation(void)
{

	DISPLAYBLOCK **activeblocksptr;
	DISPLAYBLOCK *dptr;
	int i, j;
	LIGHTBLOCK *lptr;

	if(NumActiveBlocks) {

		activeblocksptr = &ActiveBlockList[NumActiveBlocks - 1];

		for(i = NumActiveBlocks; i!=0; i--) {

			dptr = *activeblocksptr--;

			/* Deallocate Object? */

			if(dptr->ObFlags2 & ObFlag2_Deallocate) {

				DestroyActiveObject(dptr);

			}


			/* Deallocate any Lights? */

			else {

				if(dptr->ObNumLights) {

					for(j = dptr->ObNumLights - 1; j > -1; j--) {

						lptr = dptr->ObLights[j];

						if(lptr->LightFlags & LFlag_Deallocate) {

							DeleteLightBlock(dptr->ObLights[j], dptr);

						}

					}

				}

			}

		}

	}
	return 0;
}
