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

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

#include "rpskin.h"

#include "skin.h"
#include "skinskygeneric.h"

#include "SKY2_SkinGeneric/SKY2_SkinGenericData.h"
#include "SKY2_SkinGeneric/SKY2_SkinGenericNode.h"

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

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

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

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

/*===========================================================================*
 *--- Local Global Variables ------------------------------------------------*
 *===========================================================================*/
#if (!defined(DXOYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: ";
#endif /* (!defined(DXOYGEN)) */

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

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

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

/****************************************************************************
 _rpSkinPS2ManagerGenericPipeCreate

 Generic Skinning PipeLine Constructor TODO

 Inputs :
 Outputs:
 */
RxPipeline *
_rpSkinPS2ManagerGenericPipeCreate(void)
{
    RxPipeline *newPipe;
    RxLockedPipe *lpipe;
    RxNodeDefinition *manager;
    RxPipelineNode *plnode, *result;

    RWFUNCTION(RWSTRING("_rpSkinPS2ManagerGenericPipeCreate"));

    newPipe = RxPipelineCreate();
    RWASSERT(NULL != newPipe);

    newPipe->pluginId = rwID_SKINPLUGIN;

    lpipe = RxPipelineLock(newPipe);
    RWASSERT(NULL != lpipe);

    manager = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_ATOMIC);
    RWASSERT(NULL != manager);

    lpipe = RxLockedPipeAddFragment( lpipe,
                                     (RwUInt32 *)NULL,
                                     manager,
                                     (RxNodeDefinition *)NULL );
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName( lpipe,
                                       manager->name,
                                       (RxPipelineNode *)NULL,
                                       (RwInt32 *)NULL );
    RWASSERT(NULL != plnode);

    result = RxPipelineNodePS2ManagerGenerateCluster( plnode,
                                                      &RxClPS2xyz,
                                                      CL_XYZ );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2ManagerGenerateCluster( plnode,
                                                      &RxClPS2uv,
                                                      CL_UV );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2ManagerGenerateCluster( plnode,
                                                      &RxClPS2rgba,
                                                      CL_RGBA );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2ManagerGenerateCluster( plnode,
                                                      &RxClPS2normal,
                                                      CL_NORMAL );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2ManagerGenerateCluster( plnode,
                                                      &RxClPS2user1,
                                                      CL_USER1 );
    RWASSERT(NULL != result);

    RxPipelineNodePS2ManagerSetVUBufferSizes( plnode,
                                              pipeASymbStrideOfInputCluster,
                                              pipeASymbTSVertexCount,
                                              pipeASymbTLTriCount );
    lpipe = RxLockedPipeUnlock(lpipe);
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName( lpipe,
                                       manager->name,
                                       (RxPipelineNode *)NULL,
                                       (RwInt32 *)NULL );
    RWASSERT(NULL != plnode);

    result = RxPipelineNodePS2ManagerSetVIFOffset( plnode,
                                                   pipeASymbVIFOffset );
    RWASSERT(NULL != result);

    /*---- New VU Array description allowing BF Culling ----*/
    ((RxPipelineNodePS2ObjAllInOneData *)plnode->privateData)->genericVU1Index
        = TRUE;

    /*---- We now insert our own VU code -------------------*/
    result = RxPipelineNodePS2ManagerSetVU1CodeArray( plnode,
        _rpSkinSkySkinGenericTransforms );
    RWASSERT(NULL != result);

    RxPipelineNodePS2ManagerSetInstanceCallBack( plnode,
        _rxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack );

    RWRETURN(lpipe);
}

/****************************************************************************
 _rpSkinPS2AllGenericPipeCreate

 PS2All Skinning PipeLine Constructor TODO

 Inputs :
 Outputs:
 */
RxPipeline *
_rpSkinPS2AllGenericPipeCreate(RxPipeline *matPipe)
{
    RxPipeline *newPipe;
    RxLockedPipe *lpipe;
    RxNodeDefinition *ps2all;
    RxPipelineNode *plnode;

    RWFUNCTION(RWSTRING("_rpSkinPS2AllGenericPipeCreate"));
    RWASSERT(NULL != matPipe);

    newPipe = RxPipelineCreate();
    RWASSERT(NULL != newPipe);

    newPipe->pluginId = rwID_SKINPLUGIN;

    lpipe = RxPipelineLock(newPipe);
    RWASSERT(NULL != lpipe);

    ps2all = RxNodeDefinitionGetPS2All();
    RWASSERT(NULL != ps2all);

    lpipe = RxLockedPipeAddFragment( lpipe,
                                     (RwUInt32 *)NULL,
                                     ps2all,
                                     (RxNodeDefinition *)NULL );
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName( lpipe,
                                       ps2all->name,
                                       (RxPipelineNode *)NULL,
                                       (RwInt32 *)NULL );
    RWASSERT(NULL != plnode);

    lpipe = RxLockedPipeUnlock(lpipe);
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName(lpipe, ps2all->name,
                                      (RxPipelineNode *)
                                      NULL,
                                      (RwInt32 *) NULL);
    RWASSERT(NULL != plnode);

    RxPipelineNodePS2AllSetCallBack(
        plnode,
        rxPS2ALLCALLBACKOBJECTSETUP,
        _rxPipelineNodePS2SkinGenericPS2AllObjectSetupCallBack );

    /*---
    RxPipelineNodePS2AllSetCallBack(
        plnode,
        rxPS2ALLCALLBACKOBJECTFINALIZE,
        RpAtomicPS2AllObjectFinalizeCallBack );
    ---*/

    /* We make the PS2All skinning pipe just like the PS2Manager
     * one in that it's per-object.. all meshes are forced down
     * the same material pipe. There's cope for greater
     * flexibility with PS2All, this is just done to be compatible
     * more easily with code that used the PS2Manager pipe.
     *  Also, there's the lighting overloading. In order to use
     * RpSkinSetPS2Lighting with the PS2All pipeline, we have to
     * have a skinning objectSetupCB that uses the lighting
     * callback passed to that function. People should really
     * set the skinning pipeline/callbacks in their materials
     * and overload object lighting in their own pipelines. */
    RxPipelineNodePS2AllGroupMeshes(plnode, matPipe);

    RWRETURN(lpipe);
}

/****************************************************************************
 _rpSkinPS2AllMaterialGenericPipeCreate

 PS2AllMat Skinning PipeLine Constructor TODO

 Inputs :
 Outputs:
 */
RxPipeline *
_rpSkinPS2AllMaterialGenericPipeCreate(void)
{
    RxPipeline *newPipe;
    RxLockedPipe *lpipe;
    RxNodeDefinition *ps2allmat;
    RxPipelineNode *plnode, *result;

    RWFUNCTION(RWSTRING("_rpSkinPS2AllMaterialGenericPipeCreate"));

    newPipe = RxPipelineCreate();
    RWASSERT(NULL != newPipe);

    newPipe->pluginId = rwID_SKINPLUGIN;

    lpipe = RxPipelineLock(newPipe);
    RWASSERT(NULL != lpipe);

    ps2allmat = RxNodeDefinitionGetPS2AllMat();
    RWASSERT(NULL != ps2allmat);

    lpipe = RxLockedPipeAddFragment( lpipe,
                                     (RwUInt32 *)NULL,
                                     ps2allmat,
                                     (RxNodeDefinition *)NULL );
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName( lpipe,
                                       ps2allmat->name,
                                       (RxPipelineNode *)NULL,
                                       (RwInt32 *)NULL );
    RWASSERT(NULL != plnode);

    result = RxPipelineNodePS2AllMatGenerateCluster( plnode,
                                                     &RxClPS2xyz,
                                                     CL_XYZ );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2AllMatGenerateCluster( plnode,
                                                     &RxClPS2uv,
                                                     CL_UV );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2AllMatGenerateCluster( plnode,
                                                     &RxClPS2rgba,
                                                     CL_RGBA );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2AllMatGenerateCluster( plnode,
                                                     &RxClPS2normal,
                                                     CL_NORMAL );
    RWASSERT(NULL != result);

    result = RxPipelineNodePS2AllMatGenerateCluster( plnode,
                                                     &RxClPS2user1,
                                                     CL_USER1 );
    RWASSERT(NULL != result);

    RxPipelineNodePS2AllMatSetVUBufferSizes( plnode,
                                             pipeASymbStrideOfInputCluster,
                                             pipeASymbTSVertexCount,
                                             pipeASymbTLTriCount );
    lpipe = RxLockedPipeUnlock(lpipe);
    RWASSERT(NULL != lpipe);

    plnode = RxPipelineFindNodeByName( lpipe,
                                       ps2allmat->name,
                                       (RxPipelineNode *)NULL,
                                      (RwInt32 *)NULL );
    RWASSERT(NULL != plnode);

    result = RxPipelineNodePS2AllMatSetVIFOffset(plnode, pipeASymbVIFOffset);
    RWASSERT(NULL != result);

    /*---- We now insert our own VU code -------------------*/
    result = RxPipelineNodePS2AllMatSetVU1CodeArray(
        plnode,
        _rpSkinSkySkinGenericTransforms,
        VU1CODEARRAYSIZE );
    RWASSERT(NULL != result);

    /* We overload the instance and bridge CBs */
    RxPipelineNodePS2AllMatSetCallBack( plnode,
                                        rxPS2ALLMATCALLBACKMESHINSTANCETEST,
                                        RpMeshPS2AllMeshInstanceTestCallBack );

    RxPipelineNodePS2AllMatSetCallBack( plnode,
                                        rxPS2ALLMATCALLBACKRESENTRYALLOC,
                                        RpMeshPS2AllResEntryAllocCallBack );

    RxPipelineNodePS2AllMatSetCallBack(
        plnode,
        rxPS2ALLMATCALLBACKINSTANCE,
        _rxPipelineNodePS2SkinGenericPS2AllInstanceCallBack );

    RxPipelineNodePS2AllMatSetCallBack(
        plnode,
        rxPS2ALLMATCALLBACKBRIDGE,
        _rxPipelineNodePS2SkinGenericPS2AllMatBridgeCallBack );

    RWRETURN(lpipe);
}

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

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

/**
 * \ingroup rpskin
 * \ref RpSkinSetPS2Lighting
 *
 * This function allows you to overload the lighting of the
 * skinning pipeline (both the PS2Manager.csl and PS2All.csl
 * pipelines are overloaded by this function).
 *
 * If NULL is passed to this function, the default lighting
 * functionality will be reinstated.
 *
 * This function should be called after unlocking the pipeline containing
 * this node.
 *
 * \param newLightingFunc The \ref RxWorldLightingCallBack to use
 *
 * \see RxPipelineNodePS2ManagerSetLighting
 */
void
RpSkinSetPS2Lighting(RxWorldLightingCallBack newLightingFunc)
{
    SkinSkyPipeline iPipeline;
    RxNodeDefinition *manager;

    RWAPIFUNCTION(RWSTRING("RpSkinSetPS2Lighting"));

    manager = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_ATOMIC);
    RWASSERT(NULL != manager);

    for(iPipeline = rpSKINSKYPIPELINEGENERIC; 
        iPipeline < rpSKINSKYPIPELINEMAX; 
        iPipeline = (SkinSkyPipeline)(1 + (RwUInt32)iPipeline))
    {
        RxPipelineNode *plnode;

        plnode = RxPipelineFindNodeByName(
                     _rpSkinPS2ManagerPipeline(iPipeline),
                     manager->name,
                     (RxPipelineNode *)NULL,
                     (RwInt32 *)NULL );
        RWASSERT(plnode != NULL);

        RxPipelineNodePS2ObjAllInOneSetLighting(plnode, newLightingFunc);
    }

    /* The skinning PS2All ObjectSetupCB gets the callback
     * through the global gSkinLightingCallBack */
    _rpSkinPS2AllLightFunc() = newLightingFunc;

    RWRETURNVOID();
}

/**
 * \ingroup rpskin
 * \ref RpSkinGetPS2AllPipeline
 *
 * This function allows you to get a version of the skinning atomic object
 * pipeline based on the PS2All.csl node. This can then be applied to skinned
 * atomics with \ref RpAtomicSetPipeline
 *
 * \return A pointer to the PS2All skinning pipeline on success, otherwise NULL
 *
 * \see RxNodeDefinitionGetPS2All
 * \see RpAtomicSetPipeline
 */
RxPipeline *
RpSkinGetPS2AllPipeline(void)
{
    RxPipeline *pipeline;

    RWAPIFUNCTION(RWSTRING("RpSkinGetPS2AllPipeline"));

    /* TODO - what about matfx. */
    pipeline = _rpSkinPS2AllPipeline(rpSKINSKYPIPELINEGENERIC);

    RWRETURN(pipeline);
}

/**
 * \ingroup rpskinp2
 * \ref RpSkinGetPS2ManagerPipeline
 *
 * This function allows you to get a version of the skinning atomic object
 * pipeline based on the PS2Manager.csl node. This can then be applied to
 * skinned atomics with \ref RpAtomicSetPipeline
 *
 * \return A pointer to the PS2Manager skinning pipeline on success,
 * otherwise NULL
 *
 * \see RxNodeDefinitionGetPS2Manager
 * \see RpAtomicSetPipeline
 */
RxPipeline *
RpSkinGetPS2ManagerPipeline(void)
{
    RxPipeline *pipeline;

    RWAPIFUNCTION(RWSTRING("RpSkinGetPS2ManagerPipeline"));

    /* TODO - what about matfx. */
    pipeline = _rpSkinPS2ManagerPipeline(rpSKINSKYPIPELINEGENERIC);

    RWRETURN(pipeline);
}

