/****************************************************************************
 Includes
 */

#include <stdlib.h>

#include <rwcore.h>

#include "baworld.h"
#include "balight.h"
#include "bamesh.h"
#include "basector.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ = 
  "@@(#)$Id: basector.c,v 1.161 2001/09/04 11:14:30 Markj Exp $";
#endif /* (!defined(DOXYGEN)) */

/****************************************************************************
 Local Types
 */

/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */

/****************************************************************************
 Globals (across program)
 */

RwPluginRegistry    sectorTKList =
    { sizeof(RpWorldSector), 
      sizeof(RpWorldSector), 
      0, 
      0, 
      (RwPluginRegEntry *)NULL, 
      (RwPluginRegEntry *)NULL };

/****************************************************************************
 Local (static) Globals
 */

static RwModuleInfo sectorModule;

/****************************************************************************
 _rpSectorOpen

 On entry   :
 On exit    : NULL on failure
 */

void               *
_rpSectorOpen(void *instance,
              RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("_rpSectorOpen"));

    /* One more module instance */
    sectorModule.numInstances++;

    /* Success */
    RWRETURN(instance);
}

/****************************************************************************
 _rpSectorClose

 On entry   :
 On exit    : NULL on failure
 */

void               *
_rpSectorClose(void *instance,
               RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("_rpSectorClose"));

    /* One less module instance */
    sectorModule.numInstances--;

    /* Success */
    RWRETURN(instance);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                          Sector handling functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorGetNumVertices is used to retrieve the number of
 * vertices that reside within the specified atomic sector. Only vertices
 * associated with static geometry are counted.
 *
 * The world plugin must be attached before using this function.
 *
 * \param sector  Pointer to the world sector.
 *
 * \return Returns a RwInt32 value equal to the number of vertices if successful
 * or zero if there is an error or if there are no vertices.
 *
 * \see RpWorldSectorGetBBox
 * \see RpWorldSectorForAllMeshes
 * \see RpWorldSectorGetNumPolygons
 * \see RpWorldSectorGetWorld
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorGetNumVertices(const RpWorldSector * sector)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorGetNumVertices"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);

    RWRETURN(sector->numVertices);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorGetNumPolygons is used to retrieve the number of 
 * triangles that reside within the specified world sector. Only triangles 
 * associated with static geometry are counted.
 *
 * The world plugin must be attached before using this function.
 *
 * \param sector  Pointer to the world sector.
 *
 * \return Returns a RwInt32 value equal to the number of triangles if 
 * successful or zero if there is an error or if there are no triangles.
 *
 * \see RpWorldSectorGetBBox
 * \see RpWorldSectorForAllMeshes
 * \see RpWorldSectorGetNumVertices
 * \see RpWorldSectorGetWorld
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorGetNumPolygons(const RpWorldSector * sector)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorGetNumPolygons"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);

    RWRETURN(sector->numPolygons);
}


/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorRender is used to render all static objects in 
 * the specified world sector to the current camera's image raster.
 *
 * Note this function should only be called between
 * \ref RwCameraBeginUpdate and \ref RwCameraEndUpdate to ensure that 
 * any rendering that takes place is directed towards an image raster 
 * connected to a camera. 
 *
 * The world plugin must be attached before using this function.
 *
 * \param sector  Pointer to the world sector.
 *
 * \return Returns pointer to the world sector if successful or
 * NULL if there is an error.
 *
 * \see RwCameraBeginUpdate
 * \see RwCameraEndUpdate
 * \see RpWorldSetSectorRenderCallBack
 * \see RpWorldGetSectorRenderCallBack
 * \see RpWorldRender
 * \see RpClumpRender
 * \see RpAtomicRender
 * \see RpWorldPluginAttach
 *
 */
RpWorldSector      *
RpWorldSectorRender(RpWorldSector * sector)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorRender"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);

    /* App has allowed the rendering to continue */
    RWRETURN(((RpWorld *)RWSRCGLOBAL(curWorld))->renderCallBack(sector));
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorForAllMeshes is used to apply the given callback
 * function to all meshes in the given world sector.  The format of the 
 * callback function is:
 *
 * RpMesh *(*RpMeshCallBack)(RpMesh *mesh, RpMeshHeader *meshHeader, void *data);
 *
 * where data is a user-supplied data pointer to pass to the callback function.
 *
 * If any invocation of the callback function returns a failure status the
 * iteration is terminated.  However, \ref RpWorldSectorForAllMeshes will still
 * return successfully.
 *
 * The world plugin must be attached before using this function.
 *
 * \param sector  Pointer to the world sector containing the meshes.
 * \param fpCallBack  Pointer to the callback function to apply to each mesh.
 * \param pData  Pointer to the user-supplied data to pass to callback function.
 *
 * \return Returns pointer to the world sector if successful or NULL if there
 * is an error or if the world sector does not have any mesh information.
 *
 * \see RpWorldSectorGetBBox
 * \see RpWorldSectorGetNumVertices
 * \see RpGeometryForAllMeshes
 * \see RpWorldSectorGetNumPolygons
 * \see RpWorldSectorGetWorld
 * \see RpWorldPluginAttach
 *
 */
const RpWorldSector *
RpWorldSectorForAllMeshes(const RpWorldSector * sector,
                          RpMeshCallBack fpCallBack, void *pData)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorForAllMeshes"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);
    RWASSERT(fpCallBack);

    if (sector->mesh)
    {
        if (_rpMeshHeaderForAllMeshes(sector->mesh, fpCallBack, pData))
        {
            RWRETURN(sector);
        }
    }

    /* No mesh data, of callback function failed */
    RWRETURN((const RpWorldSector *)NULL);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorGetBBox is used to retrieve the specified world
 * sector's bounding box. Note that the bounding box is relative to the
 * world's bounding box which has an offset applied. The offset ensures
 * all vertex coordinates within the world's (hence world sector's) limits
 * are positive.
 *
 * The world plugin must be attached before using this function.
 *
 * \param sector  Pointer to the world sector.
 *
 * \return Returns pointer to a RwBBox value if successful or NULL if
 * there is an error.
 *
 * \see RpWorldSectorForAllMeshes
 * \see RpWorldSectorGetNumVertices
 * \see RpWorldSectorGetNumPolygons
 * \see RpWorldSectorGetWorld
 * \see RpWorldGetBBox
 * \see RpWorldGetOrigin
 * \see RpWorldCreate
 * \see RpWorldStreamRead
 * \see RpWorldPluginAttach
 *
 */
const RwBBox       *
RpWorldSectorGetBBox(const RpWorldSector * sector)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorGetBBox"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);

    RWRETURN(&sector->boundingBox);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorRegisterPlugin is used to register a plugin and 
 * reserve some space within a world sector. This must happen after the engine 
 * has been initialized but before the engine is opened.
 *
 * The world plugin must be attached before using this function.
 *
 * \param size  A RwInt32 value equal to the size of the memory block to reserve.
 * \param pluginID  A RwInt32 value equal to the plugin ID (must be unique; used
 *       to identify binary chunks).
 * \param constructCB  Constructor for the plugin data block.
 * \param destructCB  Destructor for the plugin data block.
 * \param copyCB  Copy constructor for the plugin data block.
 *
 * \return Returns RwInt32 value equal to the byte offset within the world 
 * sector of memory reserved for this plugin if successful or -1 if there
 * is an error.
 * 
 * \see RpWorldSectorValidatePlugins
 * \see RpWorldSectorRegisterPluginStream
 * \see RpWorldSectorSetStreamAlwaysCallBack
 * \see RpWorldSectorGetPluginOffset
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldValidatePlugins
 * \see RpWorldGetPluginOffset
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorRegisterPlugin(RwInt32 size, RwUInt32 pluginID,
                            RwPluginObjectConstructor constructCB,
                            RwPluginObjectDestructor destructCB,
                            RwPluginObjectCopy copyCB)
{
    RwInt32             plug;

    RWAPIFUNCTION(RWSTRING("RpWorldSectorRegisterPlugin"));
    RWASSERT(!sectorModule.numInstances);
    RWASSERT(size >= 0);

    /* Everything's cool, so pass it on */
    plug = rwPluginRegistryAddPlugin(&sectorTKList, size, pluginID,
                                     constructCB, destructCB, copyCB);
    RWRETURN(plug);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorRegisterPluginStream is used to associate a set of
 * binary stream functionality with a previously registered world sector
 * plugin.
 *
 * The world plugin must be attached before using this function.
 *
 * \param pluginID  A RwInt32 value equal to the plugin ID (must be unique; used
 *       to identify binary chunks).
 * \param readCB  Callback used when a chunk is read that is identified as being for
 *       this plugin.
 * \param writeCB  Callback used when a chunk should be written out for this plugin.
 * \param getSizeCB  Callback used to determine the binary stream size required for this
 *       plugin (return negative to suppress chunk writing).
 *
 * \return Returns a RwInt32 value equal to the byte offset within the world 
 * sector of memory reserved for this plugin if successful or -1 if there is
 * an error.
 * 
 * \see RpWorldSectorSetStreamAlwaysCallBack
 * \see RpWorldSectorValidatePlugins
 * \see RpWorldSectorRegisterPlugin
 * \see RpWorldSectorGetPluginOffset
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldGetPluginOffset
 * \see RpWorldValidatePlugins
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorRegisterPluginStream(RwUInt32 pluginID,
                                  RwPluginDataChunkReadCallBack readCB,
                                  RwPluginDataChunkWriteCallBack
                                  writeCB,
                                  RwPluginDataChunkGetSizeCallBack
                                  getSizeCB)
{
    RwInt32             plug;

    RWAPIFUNCTION(RWSTRING("RpWorldSectorRegisterPluginStream"));
    RWASSERT(readCB);
    RWASSERT(writeCB);
    RWASSERT(getSizeCB);

    /* Everything's cool, so pass it on */
    plug = rwPluginRegistryAddPluginStream(&sectorTKList, pluginID,
                                           readCB, writeCB, getSizeCB);

    RWRETURN(plug);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorSetStreamAlwaysCallBack is used to associate
 * a binary stream callback with a previously registered world sector plugin.
 * This callback is called for all plugins after stream data reading has
 * completed.
 *
 * The world plugin must be attached before using this function.
 *
 * \param pluginID  A RwInt32 value equal to the plugin ID (must be unique; used
 *       to identify binary chunks).
 * \param alwaysCB  Callback used when object base and plugin data reading is complete.
 *
 * \return Returns a RwInt32 value equal to the byte offset within the world 
 * sector of memory reserved for this plugin if successful or -1 if there is
 * an error.
 * 
 * \see RpWorldSectorRegisterPluginStream
 * \see RpWorldSectorValidatePlugins
 * \see RpWorldSectorRegisterPlugin
 * \see RpWorldSectorGetPluginOffset
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldGetPluginOffset
 * \see RpWorldValidatePlugins
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorSetStreamAlwaysCallBack(
    RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB)
{
    RwInt32             plug;

    RWAPIFUNCTION(RWSTRING("RpWorldSectorSetStreamAlwaysCallBack"));
    RWASSERT(alwaysCB);

    /* Everything's cool, so pass it on */
    plug = rwPluginRegistryAddPluginStreamAlwaysCB(
               &sectorTKList, pluginID, alwaysCB);

    RWRETURN(plug);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorSetStreamRightsCallBack is used to associate
 * a binary stream callback with a previously registered world sector plugin.
 * This callback is called for the plugin with rights after stream data
 * reading has * completed.
 *
 * The world plugin must be attached before using this function.
 *
 * \param pluginID  A RwInt32 value equal to the plugin ID (must be unique; used
 *       to identify binary chunks).
 * \param rightsCB  Callback used when object base and plugin data reading is complete.
 *
 * \return Returns a RwInt32 value equal to the byte offset within the world 
 * sector of memory reserved for this plugin if successful or -1 if there is
 * an error.
 * 
 * \see RpWorldSectorRegisterPluginStream
 * \see RpWorldSectorValidatePlugins
 * \see RpWorldSectorRegisterPlugin
 * \see RpWorldSectorGetPluginOffset
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldGetPluginOffset
 * \see RpWorldValidatePlugins
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorSetStreamRightsCallBack(RwUInt32 pluginID,
                                     RwPluginDataChunkRightsCallBack rightsCB)
{
    RwInt32             plug;

    RWAPIFUNCTION(RWSTRING("RpWorldSectorSetStreamRightsCallBack"));
    RWASSERT(rightsCB);

    /* Everything's cool, so pass it on */
    plug = _rwPluginRegistryAddPlgnStrmRightsCB(&sectorTKList, pluginID,
                                                rightsCB);

    RWRETURN(plug);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorGetPluginOffset is used to get the offset of a 
 * previously registered world sector plugin.
 *
 * The world plugin must be attached before using this function.
 * 
 * \param pluginID  A RwUInt32 value equal to the plugin ID.
 *
 * \return Returns the data block offset if successful or -1 if the plugin 
 * is not registered.
 *
 * \see RpWorldSectorRegisterPlugin
 * \see RpWorldSectorRegisterPluginStream
 * \see RpWorldSectorSetStreamAlwaysCallBack
 * \see RpWorldSectorValidatePlugins
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldGetPluginOffset
 * \see RpWorldValidatePlugins
 * \see RpWorldPluginAttach
 *
 */
RwInt32
RpWorldSectorGetPluginOffset(RwUInt32 pluginID)
{
    RwInt32             offset;

    RWAPIFUNCTION(RWSTRING("RpWorldSectorGetPluginOffset"));

    offset = rwPluginRegistryGetPluginOffset(&sectorTKList, pluginID);

    RWRETURN(offset);
}

/**
 * \ingroup rpworldsector
 * \ref RpWorldSectorValidatePlugins is used to validate the plugin memory 
 * allocated within the specified world sector. This function is useful for 
 * determining where memory trampling may be occuring within an application.
 *
 * This function only returns a meaningful response under a debug library.
 * 
 * The world plugin must be attached before using this function.
 *
 * \param  sector   Pointer to the world sector.
 *
 * \return Returns TRUE if the world sector data is valid or FALSE if there is an
 * error or if the world sector data has become corrupt.
 *
 * \see RpWorldSectorRegisterPlugin
 * \see RpWorldSectorRegisterPluginStream
 * \see RpWorldSectorSetStreamAlwaysCallBack
 * \see RpWorldSectorGetPluginOffset
 * \see RpWorldRegisterPlugin
 * \see RpWorldRegisterPluginStream
 * \see RpWorldSetStreamAlwaysCallBack
 * \see RpWorldValidatePlugins
 * \see RpWorldGetPluginOffset
 * \see RpWorldPluginAttach
 *
 */
RwBool
RpWorldSectorValidatePlugins(const RpWorldSector *  
                             __RWUNUSEDRELEASE__ sector)
{
    RWAPIFUNCTION(RWSTRING("RpWorldSectorValidatePlugins"));
    RWASSERT(sectorModule.numInstances);
    RWASSERT(sector);

#ifdef RWDEBUG
    {
        RwBool              valid;

        valid = rwPluginRegistryValidateObject(&sectorTKList, sector);

        RWRETURN(valid);
    }
#else /* RWDEBUG */
    RWRETURN(TRUE);
#endif /* RWDEBUG */
}
