//------------------------------------------------------------------------------
// File : SkyArchive.cpp
//------------------------------------------------------------------------------
// SkyWorks : Copyright 2002 Mark J. Harris and
//						The University of North Carolina at Chapel Hill
//------------------------------------------------------------------------------
// Permission to use, copy, modify, distribute and sell this software and its 
// documentation for any purpose is hereby granted without fee, provided that 
// the above copyright notice appear in all copies and that both that copyright 
// notice and this permission notice appear in supporting documentation. 
// Binaries may be compiled with this software without any royalties or 
// restrictions. 
//
// The author(s) and The University of North Carolina at Chapel Hill make no 
// representations about the suitability of this software for any purpose. 
// It is provided "as is" without express or 
// implied warranty.
/**
 * @file SkyArchive.cpp
 * 
 * Implementation of class SkyArchive.
 */
#include "SkyArchive.hpp"

#include <assert.h>

struct SkyArchiveEntry
{
  SkyArchiveEntry() : type(0), pData(NULL), iDataSize(0) {}
  unsigned char type;
  void*         pData;
  unsigned int  iDataSize;
};

struct SkyArchiveFileEntry
{
  SkyArchiveFileEntry() : type(0), iDataSize(0) {}
  unsigned char type;
  char          pName[32];
  unsigned int  iDataSize;
};


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::SkyArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::SkyArchive()
 * @brief Default constructor.  Creates an empty, unnamed archive.                                             |
 */ 
SkyArchive::SkyArchive()
: _pName(NULL)
{
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::SkyArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::SkyArchive(const char* pName)
 * @brief Constructor.  Creates an empty, named archive. 
 */ 
SkyArchive::SkyArchive(const char* pName)
{
  _pName = new char[::strlen(pName)+1];
  ::strcpy( _pName, pName);  
}


//.---------------------------------------------------------------------------.
//|   Function   : SkyArchive::SkyArchive                                 |
//|   Description: 
//.---------------------------------------------------------------------------.

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::SkyArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::SkyArchive(const SkyArchive& src)
 * @brief Copy constructor.  Deep-copies the contents of one archive to another.
 */ 
SkyArchive::SkyArchive(const SkyArchive& src)
{  
  _pName = new char[::strlen(src._pName)+1];
  ::strcpy( _pName, src._pName);
  
  _CopyDataTable( src._dataTable);
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::~SkyArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::~SkyArchive()
 * @brief Destructor.
 */ 
SkyArchive::~SkyArchive()
{
  MakeEmpty();
  SAFE_DELETE_ARRAY(_pName);
}


//.---------------------------------------------------------------------------.
//|   Function   : SkyArchive::operator=                                    |
//|   Description: |
//.---------------------------------------------------------------------------.

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::operator=
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::operator=( const SkyArchive& src)
 * @brief @todo Deep-copies the contents of one archive to another.
 */ 
SkyArchive& SkyArchive::operator=( const SkyArchive& src)
{
  if (this != &src)
  {
    MakeEmpty();
    SAFE_DELETE_ARRAY(_pName);
    _pName = new char[::strlen(src._pName)+1];
    ::strcpy( _pName, src.GetName());
    
    _CopyDataTable( src._dataTable);
  }
  return *this;
}



//=============================================================================
//  Adding Content to SkyArchive
//=============================================================================

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddData
// Description	    : 
//------------------------------------------------------------------------------
/**
* @fn SkyArchive::AddData(const char*         pName, 
SkyArchiveTypeCode  eType, 
const void*         pData, 
unsigned int        iNumBytes, 
unsigned int        iNumItems)
* @brief Adds a new data field to the archive.
* 
* Makes a copy of the data, stores it in a SkyArchiveEntry, and adds it to the
* database.  All specialized functions for the base types are implemented using 
* this function.
* 
* PARAMETERS
* @param name Field name.  This is used as the key for the database entry.
* @param type Data type of the field.
* @param pData Pointer to the data to be added to the archive.
* @param iNumBytes Size of each individual item in the data
* @param iNumItems Number of items to copy                                  
*/
SKYRESULT SkyArchive::AddData(const char*         pName, 
                              SkyArchiveTypeCode  eType, 
                              const void*         pData, 
                              unsigned int        iNumBytes, 
                              unsigned int        iNumItems /* = 1 */)
{
  // fill out a new archive entry with the supplied data
  SkyArchiveEntry* pNewEntry  = new SkyArchiveEntry;
  pNewEntry->type             = eType;
  pNewEntry->iDataSize        = iNumBytes * iNumItems;
  
  if (eType != ARCHIVE_TYPE)
  {
    pNewEntry->pData = new unsigned char[pNewEntry->iDataSize];
    ::memcpy(pNewEntry->pData, pData, pNewEntry->iDataSize);
  }
  else
  {
    pNewEntry->pData = (void*)pData;
  }
  
  char* pInternalName = new char[::strlen(pName)+1];
  ::strcpy( pInternalName, pName);
  _dataTable.insert(std::make_pair(pInternalName, pNewEntry));
  
  return SKYRESULT_OK;
}


//-----------------------------------------------------------------------------
//  SkyArchive :: AddBool( const char* pName, bool aBool)
//  SkyArchive :: AddInt8( const char* pName, Int8 anInt8)
//  SkyArchive :: AddInt16( const char* pName, Int16 anInt16)
//  SkyArchive :: AddInt32( const char* pName, Int32 anInt32)
//  ...
//-----------------------------------------------------------------------------
//
//  Specialized functions for the most common base types
//  
//-----------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddBool
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddBool(const char* pName, bool aBool)
 * @brief Adds a named bool to the archive.
 */ 
SKYRESULT SkyArchive::AddBool(const char* pName, bool aBool)
{
  return AddData( pName, BOOL_TYPE, &aBool, sizeof(bool));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddInt8
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddInt8(const char* pName, char anInt8)
 * @brief Adds a named 8-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddInt8(const char* pName, char anInt8)
{
  return AddData( pName, INT8_TYPE, &anInt8, sizeof(char));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddInt16
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddInt16(const char* pName, short anInt16)
 * @brief Adds a named 16-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddInt16(const char* pName, short anInt16)
{
  return AddData( pName, INT16_TYPE, &anInt16, sizeof(short));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddInt32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddInt32(const char* pName, int anInt32)
 * @brief Adds a named 32-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddInt32(const char* pName, int anInt32)
{
  return AddData( pName, INT32_TYPE, &anInt32, sizeof(int));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddUInt8
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddUInt8(const char* pName, unsigned char anUInt8)
 * @brief Adds a named unsigned 8-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddUInt8(const char* pName, unsigned char anUInt8)
{
  return AddData( pName, UINT8_TYPE, &anUInt8, sizeof(unsigned char));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddUInt16
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddUInt16(const char* pName, unsigned short anUInt16)
 * @brief Adds a named unsigned 16-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddUInt16(const char* pName, unsigned short anUInt16)
{
  return AddData( pName, UINT16_TYPE, &anUInt16, sizeof(unsigned short));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddUInt32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddUInt32(const char* pName, unsigned int anUInt32)
 * @brief Adds a named unsigned 32-bit integer to the archive.
 */ 
SKYRESULT SkyArchive::AddUInt32(const char* pName, unsigned int anUInt32)
{
  return AddData( pName, UINT32_TYPE, &anUInt32, sizeof(unsigned int));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddFloat32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddFloat32(const char* pName, float aFloat32)
 * @brief Adds a named 32-bit real number to the archive.
 */ 
SKYRESULT SkyArchive::AddFloat32(const char* pName, float aFloat32)
{
  return AddData( pName, FLOAT32_TYPE, &aFloat32, sizeof(float));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddFloat64
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddFloat64(const char* pName, double aFloat64)
 * @brief Adds a named 64-bit real number to the archive.
 */ 
SKYRESULT SkyArchive::AddFloat64(const char* pName, double aFloat64)
{
  return AddData( pName, FLOAT64_TYPE, &aFloat64, sizeof(double));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddString
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddString(const char* pName, const char* pString)
 * @brief Adds a named string to the archive.
 */ 
SKYRESULT SkyArchive::AddString(const char* pName, const char* pString)
{
  return AddData( pName, STRING_TYPE, pString, ::strlen(pString)+1);
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddArchive(const SkyArchive& anArchive)
 * @brief Adds a subarchive to this archive.
 *
 * This method allows hierarchical data structures to be stored in an archive.
 */
SKYRESULT SkyArchive::AddArchive(const SkyArchive& anArchive)
{
  SkyArchive* pCopy = new SkyArchive(anArchive);
  return AddData( pCopy->GetName(), ARCHIVE_TYPE, pCopy, sizeof(SkyArchive));
}

//-----------------------------------------------------------------------------
//  Adding Vector types
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddVec2f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddVec2f(const char* pName, const Vec2f& aVec2f)
 * @brief Adds a 2-component 32-bit real number vector to the archive.
 */ 
SKYRESULT SkyArchive::AddVec2f(const char* pName, const Vec2f& aVec2f)
{
  return AddData( pName, VEC2F_TYPE, &aVec2f, sizeof(Vec2f));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddVec3f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddVec3f(const char* pName, const Vec3f& aVec3f)
 * @brief Adds a 3-component 32-bit real number vector to the archive.
 */ 
SKYRESULT SkyArchive::AddVec3f(const char* pName, const Vec3f& aVec3f)
{
  return AddData( pName, VEC3F_TYPE, &aVec3f, sizeof(Vec3f));
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AddVec4f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AddVec4f(const char* pName, const Vec4f& aVec4f)
 * @brief Adds a 4-component 32-bit real number vector to the archive.
 */ 
SKYRESULT SkyArchive::AddVec4f(const char* pName, const Vec4f& aVec4f)
{
  return AddData( pName, VEC4F_TYPE, &aVec4f, sizeof(Vec4f));
}

//=============================================================================
//  Retrieving Content from SkyArchive
//=============================================================================

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindData
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindData(const char*        pName, 
                               SkyArchiveTypeCode eType, 
                               void** const       pData, 
                               unsigned int*      pNumBytes, 
                               unsigned int       index) const
 * @brief Retrieves datafield from _dataTable.
 * 
 * PARAMETERS
 * @param name The field name.  used as the key for the multimap entry.
 * @param type Data type of the field.
 * @param pData Pointer to the returned data.
 * @param pNumBytes Returns the size of the field entry returned.
 * @param index Which item of the given \a name to locate.
 */ 
SKYRESULT SkyArchive::FindData(const char*        pName, 
                               SkyArchiveTypeCode eType, 
                               void** const       pData, 
                               unsigned int*      pNumBytes, 
                               unsigned int       index /* = 0 */) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, eType);
  if (pEntry)
  {
    if (pData)
    {
      *pData = new unsigned char[pEntry->iDataSize];
      ::memcpy( ((void*)*pData), pEntry->pData, pEntry->iDataSize);
    }
    
    if (pNumBytes)
    {
      *pNumBytes = pEntry->iDataSize;
    }
    
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}


//-----------------------------------------------------------------------------
//  SkyArchive :: FindBool( const char* pName, bool* aBool)
//  SkyArchive :: FindInt8( const char* pName, Int8* anInt8)
//  SkyArchive :: FindInt16( const char* pName, Int16* anInt16)
//  SkyArchive :: FindInt32( const char* pName, Int32* anInt32)
//  ...
//-----------------------------------------------------------------------------
//
//  specialized function for the most common base types
//  
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindBool
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindBool(const char* pName, bool* pBool, unsigned int index) const
 * @brief Finds a named bool in the archive.
 */ 
SKYRESULT SkyArchive::FindBool(const char* pName, bool* pBool, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, BOOL_TYPE);
  if (pEntry)
  {
    bool* pData = (bool*)(pEntry->pData);
    *pBool      = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindInt8
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindInt8(const char* pName, char* pInt8, unsigned int index) const
 * @brief Finds a named 8-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindInt8(const char* pName, char* pInt8, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, INT8_TYPE);
  if (pEntry)
  {
    char* pData = (char*)(pEntry->pData);
    *pInt8      = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindInt16
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindInt16(const char* pName, short* pInt16, unsigned int index) const
 * @brief Finds a named 16-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindInt16(const char* pName, short* pInt16, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, INT16_TYPE);
  if (pEntry)
  {
    short* pData  = (short*)(pEntry->pData);
    *pInt16       = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindInt32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindInt32(const char* pName, int* pInt32, unsigned int index) const
 * @brief Finds a named 32-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindInt32(const char* pName, int* pInt32, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, INT32_TYPE);
  if (pEntry)
  {
    int* pData  = (int*)(pEntry->pData);
    *pInt32     = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindUInt8
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindUInt8(const char* pName, unsigned char* pUInt8, unsigned int index) const
 * @brief Finds a named unsigned 8-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindUInt8(const char* pName, unsigned char* pUInt8, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, UINT8_TYPE);
  if (pEntry)
  {
    unsigned char* pData  = (unsigned char*)(pEntry->pData);
    *pUInt8               = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindUInt16
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindUInt16(const char* pName, unsigned short* pUInt16, unsigned int index) const
 * @brief Finds a named unsigned 16-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindUInt16(const char* pName, unsigned short* pUInt16, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, UINT16_TYPE);
  if (pEntry)
  {
    unsigned short* pData = (unsigned short*)(pEntry->pData);
    *pUInt16              = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindUInt32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindUInt32(const char* pName, unsigned int* pUInt32, unsigned int index) const
 * @brief Finds a named unsigned 32-bit integer in the archive.
 */ 
SKYRESULT SkyArchive::FindUInt32(const char* pName, unsigned int* pUInt32, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, UINT32_TYPE);
  if (pEntry)
  {
    unsigned int* pData = (unsigned int*)(pEntry->pData);
    *pUInt32            = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindFloat32
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindFloat32(const char* pName, float* pFloat32, unsigned int index) const
 * @brief Finds a named 32-bit real number in the archive.
 */ 
SKYRESULT SkyArchive::FindFloat32(const char* pName, float* pFloat32, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, FLOAT32_TYPE);
  if (pEntry)
  {
    float* pData  = (float*)(pEntry->pData);
    *pFloat32     = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindFloat64
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindFloat64(const char* pName, double* pFloat64, unsigned int index) const
 * @brief Finds a named 64-bit real number in the archive.
 */ 
SKYRESULT SkyArchive::FindFloat64(const char* pName, double* pFloat64, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, FLOAT64_TYPE);
  if (pEntry)
  {
    double* pData = (double*)(pEntry->pData);
    *pFloat64     = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindString
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindString(const char* pName, char** const pString, unsigned int index) const
 * @brief Finds a named string in the archive.
 */ 
SKYRESULT SkyArchive::FindString(const char* pName, char** const pString, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, STRING_TYPE);
  if (pEntry)
  {
    char* pData = (char*)(pEntry->pData);
    *pString    = new char[pEntry->iDataSize];
    ::strcpy((char*)*pString, pData);
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindArchive(const char* pName, SkyArchive* pArchive, unsigned int index) const
 * @brief Finds a named sub-archive in the archive.
 */ 
SKYRESULT SkyArchive::FindArchive(const char* pName, SkyArchive* pArchive, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, ARCHIVE_TYPE);
  if (pEntry)
  {
    SkyArchive* pData  = (SkyArchive*)(pEntry->pData);
    *pArchive         = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindVec2f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindVec2f(const char* pName, Vec2f* pVec2f, unsigned int index) const
 * @brief Finds a 2-component 32-bit real number vector in the archive.
 */ 
SKYRESULT SkyArchive::FindVec2f(const char* pName, Vec2f* pVec2f, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, VEC2F_TYPE);
  if (pEntry)
  {
    Vec2f* pData  = (Vec2f*)(pEntry->pData);
    *pVec2f       = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindVec3f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindVec3f(const char* pName, Vec3f* pVec3f, unsigned int index) const
 * @brief Finds a 3-component 32-bit real number vector in the archive.
 */ 
SKYRESULT SkyArchive::FindVec3f(const char* pName, Vec3f* pVec3f, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, VEC3F_TYPE);
  if (pEntry)
  {
    Vec3f* pData  = (Vec3f*)(pEntry->pData);
    *pVec3f       = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::FindVec4f
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::FindVec4f(const char* pName, Vec4f* pVec4f, unsigned int index) const
 * @brief Finds a 4-component 32-bit real number vector in the archive.
 */ 
SKYRESULT SkyArchive::FindVec4f(const char* pName, Vec4f* pVec4f, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, VEC4F_TYPE);
  if (pEntry)
  {
    Vec4f* pData  = (Vec4f*)(pEntry->pData);
    *pVec4f       = *pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::AccessArchive
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::AccessArchive(const char* pName, SkyArchive** pArchive, unsigned int index) const
 * @brief Accesses a named sub-archive in an archive directly.
 * 
 * Note: The data are not copied!
 */ 
SKYRESULT SkyArchive::AccessArchive(const char* pName, SkyArchive** pArchive, unsigned int index) const
{
  const SkyArchiveEntry* pEntry = _FindEntry(pName, index, ARCHIVE_TYPE);
  if (pEntry)
  {
    SkyArchive* pData = (SkyArchive*)(pEntry->pData);
    *pArchive        = pData;
    return SKYRESULT_OK;
  }
  return SKYRESULT_FAIL;
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::GetInfo
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::GetInfo(const char* pName, SkyArchiveTypeCode eType, unsigned int* pNumFound) const
 * @brief Computes the number of fields that contain the given name and type.
 * 
 * PARAMETERS
 * @param pName Field name to search for.
 * @param eType Field type to search for.
 * @param pNumFound Returns the number of fields that contain given name and type.
 */ 
SKYRESULT SkyArchive::GetInfo(const char*         pName, 
                              SkyArchiveTypeCode  eType, 
                              unsigned int*       pNumFound) const
{
  //
  // Find the range of entries in the mmap with the key matching pName
  //
  std::pair<SkyMMapConstIter, SkyMMapConstIter> b = _dataTable.equal_range((char*)pName);
  
  unsigned int count = 0;
  for ( SkyMMapConstIter i = b.first; i != b.second; ++i )
  {
    //
    // The entry's type must match...
    //
    const SkyArchiveEntry* pEntry = (*i).second;
    if (pEntry->type == eType || ANY_TYPE == eType)
    {
      // only increment the count when the type matches
      ++count;
    }
  }
  
  if (pNumFound)
  {
    *pNumFound = count;
  }
  
  if (0 == count)
    return SKYRESULT_FAIL;
  
  return SKYRESULT_OK;
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::GetInfo
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::GetInfo(unsigned int iNameIndex, char** pNameFound, SkyArchiveTypeCode* pTypeCode, unsigned int* pNumFound)
 * @brief Returns information about the key at the specified index.
 * 
 * PARAMETERS
 * @param nameIndex Key index to look up.
 * @param pNameFound Key name is returned here.
 * @param pTypeCode Key type is returned here.
 * @param pNumFound Number of fields held under key name is returned here.
 */ 
SKYRESULT SkyArchive::GetInfo(unsigned int        iNameIndex, 
                              char**              pNameFound, 
                              SkyArchiveTypeCode* pTypeCode,
                              unsigned int*       pNumFound)
{ 
  assert( pNameFound != NULL);
  
  if (!pNameFound)
    return SKYRESULT_FAIL;

  unsigned int      iCurrentKeyIndex = 0;
  SkyMMapConstIter  iter;
  const char*       pLastKey = "";
  
  for (iter = _dataTable.begin(); iter != _dataTable.end(); iter++)
  {
    const char* pKey = (*iter).first;
    if (::strcmp( pLastKey, pKey))
    {
      if (iCurrentKeyIndex == iNameIndex)
      {
        *pNameFound = new char[::strlen(pKey) + 1];
        ::strcpy(*pNameFound, pKey);
        
        if (pTypeCode)
        {
          const SkyArchiveEntry* pEntry = (*iter).second;
          *pTypeCode = (SkyArchiveTypeCode)pEntry->type;
        }
        
        if (pNumFound)
        {
          return GetInfo( *pNameFound, *pTypeCode, pNumFound);
        }
        return SKYRESULT_OK;
      }
      
      pLastKey = pKey;
      ++iCurrentKeyIndex;
    }
  }
  return SKYRESULT_FAIL;
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::GetNumUniqueNames
// Description	    : 
//------------------------------------------------------------------------------
/**
* @fn SkyArchive::GetNumUniqueNames() const
* @brief Computes the number of unique key names in _dataTable.
*/ 
unsigned int SkyArchive::GetNumUniqueNames() const
{
  // duh!
  if (IsEmpty())
    return 0;
  
  unsigned int      iNumKeys = 0;
  SkyMMapConstIter  iter;
  const char*       pLastKey = "";
  
  for (iter = _dataTable.begin(); iter != _dataTable.end(); iter++)
  {
    const char* pKey = (*iter).first;
    if (::strcmp( pLastKey, pKey))
    {
      ++iNumKeys;
      pLastKey = pKey;
    }
  }
  return iNumKeys;
}


//=============================================================================
//  Removing Contents of SkyArchive
//=============================================================================

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::MakeEmpty
// Description	    : 
//------------------------------------------------------------------------------
/**
* @fn SkyArchive::MakeEmpty()
* @brief Remove all the contents of the database. 
*/ 
SKYRESULT SkyArchive::MakeEmpty()
{
  SkyMMapIter iter;
  
  for (iter = _dataTable.begin(); iter != _dataTable.end(); iter++)
  {
    SkyArchiveEntry* pEntry = (*iter).second;
    char* pName = (*iter).first;
    SAFE_DELETE_ARRAY(pName);
    
    if (ARCHIVE_TYPE == pEntry->type)
    {
      SkyArchive* pArchive = (SkyArchive*)(pEntry->pData);
      SAFE_DELETE(pArchive);
    }
    else
    {
      SAFE_DELETE_ARRAY(pEntry->pData);
    }
    SAFE_DELETE(pEntry);
  }
  
  _dataTable.clear();
  
  return SKYRESULT_OK;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::IsEmpty
// Description	    : 
//------------------------------------------------------------------------------
/**
* @fn SkyArchive::IsEmpty() const
* @brief returns true if the archive is empty, false if it contains any data.
*/ 
bool SkyArchive::IsEmpty() const
{
  return (0 == _dataTable.size());
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::Load
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::Load(const char* pFileName)
 * @brief Load the contents of a SkyArchive from file storage.
 */ 
SKYRESULT SkyArchive::Load(const char* pFileName)
{
  if (!pFileName) {
    FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::Load(): file name is NULL.");
  	}
  FILE* pSrcFile = NULL;
  
  char buf[512];
  sprintf(buf,"SkyArchive::Load(%s)",pFileName);
  SkyTrace(buf);
  if (NULL == (pSrcFile = fopen(pFileName, "rb"))) // file opened successfully   
  {
    SkyTrace("Error: SkyArchive::Load(): failed to open file for reading.");
    return SKYRESULT_FAIL;
  }
    
  SKYRESULT retVal = _Load(pSrcFile);
  fclose(pSrcFile);

  FAIL_RETURN(retVal);
  return SKYRESULT_OK;
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::Commit
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::Save(const char* pFilename) const
 * @brief Commit Contents of SkyArchive to file storage.                 
 */ 
SKYRESULT SkyArchive::Save(const char* pFileName) const
{
  if (!pFileName)
    FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::Save(): file name is NULL.");

  FILE* pDestFile = NULL;
  
  if (NULL == (pDestFile = fopen(pFileName, "wb"))) // file opened successfully   
    FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::Save(): failed to open file for writing."); 
  
  SKYRESULT retVal = _Save(pDestFile);
  fflush(pDestFile);
  fclose(pDestFile);

  FAIL_RETURN(retVal);
  return SKYRESULT_OK;
}


//=============================================================================
// Private helper functions
//=============================================================================

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::_FindEntry
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::_FindEntry(const char* pName, unsigned int index, SkyArchiveTypeCode  eType) const
 * @brief Locates and returns the SkyArchiveEntry with the specified name, index, and type.                                                                
 * 
 * PARAMETERS
 * @param pName Entry name to locate (this is used as the database key)
 * @param index Entry index to locate (in case of multiple entries)
 * @param type Entry must have this type (@see SkyArchiveTypeCode)
 *
 * Returns a pointer to the entry or NULL if no matching entry could be located.
 */ 
const SkyArchiveEntry* SkyArchive::_FindEntry(const char*         pName, 
                                              unsigned int        index, 
                                              SkyArchiveTypeCode  eType) const
{
  //
  // Find the range of entries in the mmap with the key matching /name/
  //
  std::pair< SkyMMapConstIter, SkyMMapConstIter > b = _dataTable.equal_range((char*)pName);
  
  unsigned int count = 0;
  for (SkyMMapConstIter i = b.first; (i != b.second) && (count <= index); ++i)
  {
    //
    // The entry's type and index must match...
    //
    const SkyArchiveEntry* pEntry = (*i).second;
    if (pEntry->type == eType)
    {
      if (count == index)
      {
        return pEntry;
      }
      
      // only increment the count when the type matches
      ++count;
    }
  }
  return NULL;
}


//.---------------------------------------------------------------------------.
//|   Function   : SkyArchive::_CopyDataTable                               |
//|   Description:                                                            |
//.---------------------------------------------------------------------------.
void SkyArchive::_CopyDataTable( const SkyArchiveMMap& src)
{
  SkyMMapConstIter iter;
  
  for (iter = src.begin(); iter != src.end(); iter++)
  {
    const SkyArchiveEntry* pSrcEntry  = (*iter).second;
    const char* pSrcName              = (*iter).first;
    
    if (ARCHIVE_TYPE == pSrcEntry->type)
    {
      SkyArchive* pSrcArchive = (SkyArchive*)pSrcEntry->pData;
      AddArchive(*pSrcArchive);
    }
    else
    {
      SkyArchiveEntry* pNewEntry    = new SkyArchiveEntry;
      pNewEntry->type               = pSrcEntry->type;
      pNewEntry->iDataSize          = pSrcEntry->iDataSize;
      pNewEntry->pData              = new unsigned char[pNewEntry->iDataSize];
      ::memcpy( pNewEntry->pData, pSrcEntry->pData, pNewEntry->iDataSize);
      
      char* pName = new char[::strlen(pSrcName)+1];
      ::strcpy( pName, pSrcName );
      _dataTable.insert(std::make_pair(pName, pNewEntry));
    }
  }
}

//------------------------------------------------------------------------------
// Function     	  : SkyArchive::_Save
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyArchive::_Save(FILE* pDestFile) const
 * @brief Saves data to a file (possibly recursively in the case of subarchives).
 */ 
SKYRESULT SkyArchive::_Save(FILE* pDestFile) const
{
  // fill out a record for this archive & write it
  SkyArchiveFileEntry me;
  me.type       = ARCHIVE_TYPE;
  ::strncpy( me.pName, _pName, 32);
  me.iDataSize  = _dataTable.size();
  
  size_t iNumItemsWritten = fwrite((const void*)&me, sizeof(SkyArchiveFileEntry), 1, pDestFile);
  
  if (1 > iNumItemsWritten)
    FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Save(): failed to write Archive header.");
  
  SkyMMapConstIter iter;
  for (iter = _dataTable.begin(); iter != _dataTable.end(); iter++)
  {
    // fill out a record for each item in _dataTable & write it
    const SkyArchiveEntry* pEntry = (*iter).second;
    switch(pEntry->type)
    {
    case ARCHIVE_TYPE:
      {
        ((SkyArchive*)(pEntry->pData))->_Save(pDestFile);
        break;
      }
      
    default:
      {
        SkyArchiveFileEntry item;
        item.type       = pEntry->type;
        ::strncpy( item.pName, (*iter).first, 32);
        item.iDataSize  = pEntry->iDataSize;
        
        iNumItemsWritten = fwrite((const void*)&item, sizeof(SkyArchiveFileEntry), 1, pDestFile);
        if (1 > iNumItemsWritten)
          FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Save(): failed to write Archive Entry header.");
        iNumItemsWritten = fwrite((const void*)pEntry->pData, pEntry->iDataSize, 1, pDestFile);
        if (1 > iNumItemsWritten)
          FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Save(): failed to write Archive Entry data.");
        break;
      }
    }
  }
  return SKYRESULT_OK;
}


//------------------------------------------------------------------------------
// Function     	  : SkyArchive::_Load
// Description	    : 
//------------------------------------------------------------------------------
/**
* @fn SkyArchive::_Load( FILE* pSrcFile)
* @brief Loads data from a file (possibly recursively in the case of subarchives).
*/ 
SKYRESULT SkyArchive::_Load( FILE* pSrcFile)
{
  // first make sure the file is open and readable.
  
  // load the first record
  SkyArchiveFileEntry thisItem;
  size_t iNumItemsRead = fread((void*)&thisItem, sizeof(SkyArchiveFileEntry), 1, pSrcFile);
  if (1 > iNumItemsRead)
    FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Load(): failed to read Archive header.");
  
  _pName = new char[::strlen(thisItem.pName)+1];
  ::strcpy( _pName, thisItem.pName);
    
  for (unsigned int iNumItems = 0; iNumItems < thisItem.iDataSize; ++iNumItems)
  {
    SkyArchiveFileEntry embeddedItem;
    long iFileLoc = ftell(pSrcFile);  // store location before the read
    iNumItemsRead = fread((void*)&embeddedItem, sizeof(SkyArchiveFileEntry), 1, pSrcFile);
    if (1 > iNumItemsRead)
      FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Load(): failed to read embedded archive item.");
   
    switch( embeddedItem.type)
    {
    case ARCHIVE_TYPE:
      {
        if (0 != fseek(pSrcFile, iFileLoc, SEEK_SET))
          FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Load(): failed to set the file position.");
        SkyArchive newArchive;
        newArchive._Load(pSrcFile); // recursively load the subarchive
        AddArchive(newArchive);     // add the loaded archive to the database in memory.
      }
      break;
    default:
      {
        void* pData = new unsigned char[embeddedItem.iDataSize];
        iNumItemsRead = fread((void*)pData, embeddedItem.iDataSize, 1, pSrcFile);
        if (1 > iNumItemsRead)
          FAIL_RETURN_MSG(SKYRESULT_FAIL, "Error: SkyArchive::_Load(): failed to read item data.");
        AddData( embeddedItem.pName, 
                (SkyArchiveTypeCode)embeddedItem.type, 
                pData, 
                embeddedItem.iDataSize);
        delete[] pData;
        break;
      }      
    }
  }
  return SKYRESULT_OK;
}
