/*===========================================================================*
 *-                                                                         -*
 *-  Module  :   patchatomic.c                                              -*
 *-                                                                         -*
 *-  Purpose :   General patch handling.                                    -*
 *-                                                                         -*
 *===========================================================================*/

/*===========================================================================*
 *--- Include files ---------------------------------------------------------*
 *===========================================================================*/
#include <rwcore.h>
#include <rpworld.h>

#include "rpplugin.h"
#include "rpdbgerr.h"

#include "rppatch.h"

#include "patchatomic.h"
#include "patchgeometry.h"
#include "patchstream.h"
#include "patch.h"

/*===========================================================================*
 *--- Private Types ---------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private Global Variables ----------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private Defines -------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Local Types -----------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Local Global Variables ------------------------------------------------*
 *===========================================================================*/
#if (!defined(DXOYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: patchatomic.c,v 1.8 2001/10/03 11:01:33 jamesa Exp $";
#endif /* (!defined(DXOYGEN)) */

/*===========================================================================*
 *--- Local Defines ---------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Local functions -------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private functions -----------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Plugin Engine Functions -----------------------------------------------*
 *===========================================================================*/

/*****************************************************************************
 _rpPatchAtomicConstructor

 TODO

 Inputs:  object -
          offset -
          size   -
 Outputs: void * -
 */
void *
_rpPatchAtomicConstructor( void *object,
                           RwInt32 offset __RWUNUSED__,
                           RwInt32 size __RWUNUSED__ )
{
    RpAtomic *atomic;
    PatchAtomicData *atomicData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicConstructor"));
    RWASSERT(NULL != object);

    atomic = (RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);

    atomicData->patchMesh = (PatchMesh *)NULL;
    atomicData->lod.callback = _rpPatchLODAtomicDefaultSelectLOD;
    atomicData->lod.userData = (RpPatchLODUserData)NULL;

    RWRETURN(object);
}

/*****************************************************************************
 _rpPatchAtomicDestructor

 TODO

 Inputs:  object -
          offset -
          size   -
 Outputs: void * -
 */
void *
_rpPatchAtomicDestructor( void *object,
                          RwInt32 offset __RWUNUSED__,
                          RwInt32 size __RWUNUSED__ )
{
    RpAtomic *atomic;
    PatchAtomicData *atomicData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicDestructor"));
    RWASSERT(NULL != object);

    atomic = (RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);

    if(NULL != atomicData->patchMesh)
    {
        PatchMesh *mesh;
        RwBool success;

        mesh = _rpPatchMeshGetInternal(atomicData->patchMesh);
        RWASSERT(NULL != mesh);

        success = _rpPatchMeshDestroy(mesh);
        RWASSERT(TRUE == success);
    }

    atomicData->patchMesh = (PatchMesh *)NULL;
    atomicData->lod.callback = (RpPatchLODCallBack)NULL;
    atomicData->lod.userData = (RpPatchLODUserData)NULL;

    RWRETURN(object);
}

/*****************************************************************************
 _rpPatchAtomicCopy

 TODO

 Inputs: dstObject -
         srcObject -
         offset    -
         size      -
 Outputs: void * -
 */
void *
_rpPatchAtomicCopy( void *dstObject,
                    const void *srcObject,
                    RwInt32 offset __RWUNUSED__,
                    RwInt32 size __RWUNUSED__ )
{
    const RpAtomic *srcAtomic;
    RpAtomic *dstAtomic;
    const PatchAtomicData *srcAtomicData;
    PatchAtomicData *dstAtomicData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicCopy"));
    RWASSERT(NULL != dstObject);
    RWASSERT(NULL != srcObject);

    srcAtomic = (const RpAtomic *)srcObject;
    RWASSERT(NULL != srcAtomic);
    dstAtomic = (RpAtomic *)dstObject;
    RWASSERT(NULL != dstAtomic);
    srcAtomicData = PATCHATOMICGETCONSTDATA(srcAtomic);
    RWASSERT(NULL != srcAtomicData);
    dstAtomicData = PATCHATOMICGETDATA(dstAtomic);
    RWASSERT(NULL != dstAtomicData);

    dstAtomicData->patchMesh = srcAtomicData->patchMesh;
    dstAtomicData->lod.callback = srcAtomicData->lod.callback;
    dstAtomicData->lod.userData = srcAtomicData->lod.userData;

    if(NULL != dstAtomicData->patchMesh)
    {
        PatchMesh *mesh;

        mesh = _rpPatchMeshGetInternal(dstAtomicData->patchMesh);
        RWASSERT(NULL != mesh);

        mesh = _rpPatchMeshAddRef(mesh);
        RWASSERT(NULL != mesh);
    }

    RWRETURN(dstObject);
}

/*****************************************************************************
 _rpPatchAtomicGetSize

 TODO

 Inputs: object -
         offset -
         size   -
 Outputs: void * -
 */
RwInt32
_rpPatchAtomicGetSize( const void *object,
                       RwInt32 offset __RWUNUSED__,
                       RwInt32 size __RWUNUSED__ )
{
    const RpAtomic *atomic;
    const PatchAtomicData *atomicData;

    RwInt32 sizeTotal;

    RWFUNCTION(RWSTRING("_rpPatchAtomicGetSize"));
    RWASSERT(NULL != object);

    atomic = (const RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETCONSTDATA(atomic);
    RWASSERT(NULL != atomicData);

    /* Nothing to do, at the moment.  */
    sizeTotal = 0;

    RWRETURN(sizeTotal);
}

/*****************************************************************************
 _rpPatchAtomicRead

 TODO

 Inputs: stream -
         length -
         object -
         offset -
         size   -
 Outputs: RwStream * -
 */
RwStream *
_rpPatchAtomicRead( RwStream *stream,
                    RwInt32 length __RWUNUSED__,
                    void *object,
                    RwInt32 offset __RWUNUSED__,
                    RwInt32 size __RWUNUSED__ )
{
    RpAtomic *atomic;
    PatchAtomicData *atomicData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicRead"));
    RWASSERT(NULL != stream);
    RWASSERT(NULL != object);

    atomic = (RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);

    /* Nothing to do, at the moment.  */

    RWRETURN(stream);
}

/*****************************************************************************
 _rpPatchAtomicWrite

 TODO

 Inputs: stream -
         length -
         object -
         offset -
         size   -
 Outputs: RwStream *
 */
RwStream *
_rpPatchAtomicWrite( RwStream *stream,
                     RwInt32 length __RWUNUSED__,
                     const void *object,
                     RwInt32 offset __RWUNUSED__,
                     RwInt32 size __RWUNUSED__ )
{
    const RpAtomic *atomic;
    const PatchAtomicData *atomicData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicWrite"));
    RWASSERT(NULL != stream);
    RWASSERT(NULL != object);

    atomic = (const RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETCONSTDATA(atomic);
    RWASSERT(NULL != atomicData);

    /* Nothing to do, at the moment.  */

    RWRETURN(stream);
}

/*****************************************************************************
 _rpPatchAtomicAlways

 TODO

 Inputs: object -
         offset -
         size   -
 Outputs: RwBool -
 */
RwBool
_rpPatchAtomicAlways( void *object,
                      RwInt32 offset __RWUNUSED__,
                      RwInt32 size   __RWUNUSED__ )
{
    RpAtomic *atomic;
    PatchAtomicData *atomicData;
    RpGeometry *geometry;
    PatchGeometryData *geometryData;

    RWFUNCTION(RWSTRING("_rpPatchAtomicAlways"));
    RWASSERT(NULL != object);

    /* Grab the atomic and atomicData. */
    atomic = (RpAtomic *)object;
    RWASSERT(NULL != atomic);
    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);
    geometry = RpAtomicGetGeometry(atomic);
    RWASSERT(NULL != geometry);
    geometryData = PATCHGEOMETRYGETDATA(geometry);
    RWASSERT(NULL != geometryData);

    /* Get the patch mesh. */
    atomicData->patchMesh = geometryData->mesh;

    if(NULL != atomicData->patchMesh)
    {
        PatchMesh *mesh;

        /* Add a ref count. */
        mesh = _rpPatchMeshAddRef(atomicData->patchMesh);
        RWASSERT(NULL != mesh);

        atomic = _rpPatchPipelinesAttach(atomic);
        RWASSERT(NULL != atomic);
    }

    RWRETURN(TRUE);
}

/*===========================================================================*
 *--- Plugin API Functions --------------------------------------------------*
 *===========================================================================*/

/**
 * \ingroup rppatch
 * \ref RpPatchAtomicSetPatchMesh
 * is used to attach the specified patch mesh to the given atomic. Setting the
 * patch mesh also sets the atomic's bounding sphere equal to the bounding
 * sphere of the patch mesh.
 *
 * If a patch mesh is already attached to the given atomic then that patch
 * mesh is destroyed (unless it is still used by another atomic) before the
 * new one is added.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param atomic    Pointer to the atomic.
 * \param patchMesh Pointer to the patch mesh.
 *
 * \return A pointer to the \ref RpAtomic if successful, or NULL otherwise.
 *
 * \see RpPatchAtomicGetPatchMesh
 * \see RpPatchMeshCreate
 * \see RpPatchMeshLock
 * \see RpPatchMeshUnlock
 */
RpAtomic *
RpPatchAtomicSetPatchMesh( RpAtomic *atomic,
                           RpPatchMesh *patchMesh )
{
    PatchMesh *mesh;
    PatchAtomicData *atomicData;
    RpGeometry *geometry;

    RWAPIFUNCTION(RWSTRING("RpPatchAtomicSetPatchMesh"));
    RWASSERT(0 < _rpPatchGlobals.module.numInstances);
    RWASSERT(NULL != atomic);

    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);

    mesh = _rpPatchMeshGetInternal(patchMesh);

    if(mesh != atomicData->patchMesh)
    {
        if(NULL != mesh)
        {
            /* Add a ref to the patch mesh. */
            mesh = _rpPatchMeshAddRef(mesh);
        }

        if(NULL != atomicData->patchMesh)
        {
            /* Destroy the old patch mesh. */
            _rpPatchMeshDestroy(atomicData->patchMesh);
        }

        /* Store the mesh. */
        atomicData->patchMesh = mesh;

        /* Check the geometry. */
        geometry = _rpPatchMeshGetGeometry(mesh);
        RWASSERT(NULL != geometry);

        atomic = RpAtomicSetGeometry(atomic, geometry, 0);
        RWASSERT(NULL != atomic);

        /* Setup the pipeline. */
        atomic = _rpPatchPipelinesAttach(atomic);
        RWASSERT(NULL != atomic);
    }

    RWRETURN(atomic);
}

/**
 * \ingroup rppatch
 * \ref RpPatchAtomicGetPatchMesh
 * is used to retrieve the patch mesh referenced by the specified atomic.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param atomic Pointer to the atomic containing the patch mesh.
 *
 * \return A pointer to the atomic's \ref RpPatchMesh if successful, or NULL
 * if there is an error or if no patch mesh is defined.
 *
 * \see RpPatchAtomicSetPatchMesh
 */
RpPatchMesh *
RpPatchAtomicGetPatchMesh( RpAtomic *atomic )
{
    RpPatchMesh *patchMesh;
    PatchAtomicData *atomicData;

    RWAPIFUNCTION(RWSTRING("RpPatchAtomicGetPatchMesh"));
    RWASSERT(0 < _rpPatchGlobals.module.numInstances);
    RWASSERT(NULL != atomic);

    atomicData = PATCHATOMICGETDATA(atomic);
    RWASSERT(NULL != atomicData);

    patchMesh = _rpPatchMeshGetExternal(atomicData->patchMesh);

    RWRETURN(patchMesh);
}
