/*===========================================================================
 *
 * File:	Dfarchdt.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Friday, June 29, 2001
 *
 * Contains the primary CDFArch3D object and routines used to load 3D
 * objects from that file.
 *
 *=========================================================================*/

	/* Include Files */
#include "dfarchdt.h"
#include "uesp/dagger/bsa/dfarch3d.h"


/*===========================================================================
 *
 * Begin Local Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();

	/* Main Arch3D file object */
  CDFArch3dFile l_DFArch3DFile;

	/* Cache of 3D objects */
  CDF3dObject** l_ppDF3dObjectCache = NULL;
  int           l_NumDF3dObjectsCache = 0;

/*===========================================================================
 *		End of Local Variable Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void AddDF3dObjectCache (pObject, Index);
 *
 * Attempts to add the given object to the 3D cache of objects.  If the 
 * given index is occupied, the current cache object is destroyed.
 *
 *=========================================================================*/
void AddDF3dObjectCache (CDF3dObject* pObject, const int Index) {
  DEFINE_FUNCTION("AddDF3dObjectCache()");

	/* Ensure valid input */
  ASSERT(pObject != NULL && Index >= 0 && Index < DFARCH3D_DATA_CACHESIZE);
  ASSERT(l_ppDF3dObjectCache != NULL);

	/* Delete current cache object, if any */
  DestroyPointer(l_ppDF3dObjectCache[Index]);

	/* Set the cache object */
  l_ppDF3dObjectCache[Index] = pObject;
 }
/*===========================================================================
 *		End of Function AddDF3dObjectCache()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void CreateDF3dObjectCache (void);
 *
 * Allocates the global Arch3D object cache.
 *
 *=========================================================================*/
void CreateDF3dObjectCache (void) {
  DEFINE_FUNCTION("CreateDF3dObjectCache()");
  static boolean RegisterAtExit = FALSE;
  
	/* Ensure valid application state */
  if (l_ppDF3dObjectCache != NULL) return;

	/* Create the array of pointers and initialize */
  CreateArrayPointer(l_ppDF3dObjectCache, CDF3dObject*, DFARCH3D_DATA_CACHESIZE);
  memset(l_ppDF3dObjectCache, 0, sizeof(CDF3dObject*)*DFARCH3D_DATA_CACHESIZE);
  l_NumDF3dObjectsCache = 0;

	/* Register the at exit function to destroy array */
  atexit(DestroyDF3dObjectCache);
  RegisterAtExit = TRUE;
 }
/*===========================================================================
 *		End of Function CreateDF3dObjectCache()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void ClearDF3dObjectCache (void);
 *
 * Removes any allocated 3D objects from the cache.
 *
 *=========================================================================*/
void ClearDF3dObjectCache (void) {
  DEFINE_FUNCTION("ClearDF3dObjectCache()");
  int Index;

  	/* Ignore if no cache to destroy */
  if (l_ppDF3dObjectCache == NULL) return;

	/* Delete any allocated 3D objects */
  for (Index = 0; Index < DFARCH3D_DATA_CACHESIZE; Index++) {
    DestroyPointer(l_ppDF3dObjectCache[Index]);
   }

  l_NumDF3dObjectsCache = 0;
 }
/*===========================================================================
 *		End of Function ClearDF3dObjectCache()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void DestroyDF3dObjectCache (void);
 *
 * Delete any cached 3D Objects loaded from the Arch3D file.
 *
 *=========================================================================*/
void DestroyDF3dObjectCache (void) {

	/* Ignore if no cache to destroy */
  if (l_ppDF3dObjectCache == NULL) return;

	/* Clear and destroy the array */
  ClearDF3dObjectCache();
  DestroyArrayPointer(l_ppDF3dObjectCache);
 }
/*===========================================================================
 *		End of Function DestroyDF3dObjectCache()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void DestroyDFArch3d (void);
 *
 * Delete the Arch3D file information.
 *
 *=========================================================================*/
void DestroyDFArch3d (void) {
  DestroyDF3dObjectCache();
  l_DFArch3DFile.Destroy();
 }
/*===========================================================================
 *		End of Function DestroyDFArch3dCache()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean GetDF3dObject (ppObject, ObjectValue);
 *
 * Attempt to retrieve the 3D object with the given object value from the
 * Arch3D.BSA file.  Returns the cached object if it exists, otherwise the
 * object is loaded.
 *
 *=========================================================================*/
boolean GetDF3dObject (CDF3dObject** ppObject, const long ObjectValue) {
  DEFINE_FUNCTION("GetDF3dObject()");
  boolean Result;
  int     Index;

	/* Ensure the file has been opened */
  ASSERT(l_DFArch3DFile.IsOpen() && l_ppDF3dObjectCache != NULL);

	/* Attempt to find BSA record index from object value */
  Index = l_DFArch3DFile.FindRecord(ObjectValue);
  if (Index < 0) return (FALSE);

	/* Does the cached object exist? */
  if (l_ppDF3dObjectCache[Index] != NULL) {
    *ppObject = l_ppDF3dObjectCache[Index];
    return (TRUE);
   }

	/* Attempt to load object by its index */
  Result = l_DFArch3DFile.LoadObject(ppObject, Index);
  if (!Result) return (Result);

	/* Add loaded object to the cache */
  AddDF3dObjectCache(*ppObject, Index); 
  return (TRUE);
 }
/*===========================================================================
 *		End of Function GetDF3dObject()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean InitDFArch3d (pFilename);
 *
 * Attempt to open the Daggerfall Arch3D.BSA file with the given filename
 * and read in the BSA directory contents.  The file is kept open.  Returns
 * FALSE on any error.  Initializes the 3D object cache.
 *
 *=========================================================================*/
boolean InitDFArch3d (const char* pFilename) {
  DEFINE_FUNCTION("InitDFArch3d()");
  boolean Result;

	/* Initialize the 3D object cache */
  CreateDF3dObjectCache();

	/* Open file and load the BSA directory */
  Result = l_DFArch3DFile.Open(pFilename, "rb");
  return (Result);
 }
/*===========================================================================
 *		End of Function InitDFArch3d()
 *=========================================================================*/