/*
 * Particle System plug-in
 */

/* Sony lib includes. */
#include <eekernel.h>
#include <eetypes.h>
#include <eeregs.h>
#include <libgraph.h>
#include <libdma.h>
#include <libdev.h>

/* RW includes */
#include "rpplugin.h"
#include <rpdbgerr.h>
#include <rwcore.h>
#include <rpworld.h>
#include <rpprtsys.h>

#include "prvprtcl.h"
#include "ppprtclssky.h"
#include "stddata.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: ppprtclssky.c,v 1.36 2001/09/25 15:34:45 iestynb Exp $";
#endif /* (!defined(DOXYGEN)) */

/**
 * \defgroup rpprtsysskyp2 PS2
 * \ingroup rpprtsys
 *
 * Submitting object space particles to PS2 in custom pipelines 
 * 
 * Copyright (c) Criterion Software Limited
 */

typedef union packL packL;

union packL
{
    RwReal              floats[4];
    RwInt32             ints[4];
    u_long128           pack;
};

RxNodeDefinition   *RxNodeDefinitionGetPS2ParticlesData(void);

#define SPRITESCALE (((RwReal)100))

static void        *vucode[VU1CODEARRAYSIZE];

#if (0)
static RwUInt32     GTextureON = 0x1l;
#endif /* (0) */

static RxPipeline  *
SkyParticlePipeCreate(void)
{
    RxPipeline         *newPipe = NULL;
    RxLockedPipe       *lpipe = NULL;
    RwInt32             i;

    RWFUNCTION(RWSTRING("SkyParticlePipeCreate"));

    /* Set most of the transforms to be null */
    for (i = 0; i < VU1CODEARRAYSIZE; i++)
    {
        vucode[i] = &mynulltransform;
    }

    /* We then set the transforms we use */
    /* Naughtily, we set both cull and non cull tristrip to be non cull */
    vucode[TRANSNFOG | TRANSNCL | TRANSLIST | TRANSPER] = &particles;
    vucode[TRANSNFOG | TRANSCLIP | TRANSLIST | TRANSPER] =
        &particlescull;

    newPipe = RxPipelineCreate();
    if (newPipe)
    {
        lpipe = RxPipelineLock(newPipe);

        if (NULL != lpipe)
        {
            /* STEP 1 */
            RxNodeDefinition   *allInOne =
                RxNodeDefinitionGetPS2ObjAllInOne(rxOBJTYPE_ATOMIC);
            RxNodeDefinition   *instance =
                RxNodeDefinitionGetPS2MatInstance();
            RxNodeDefinition   *bridge =
                RxNodeDefinitionGetPS2MatBridge();
            RxNodeDefinition   *instuserdata =
                RxNodeDefinitionGetPS2ParticlesData();
            RxPipelineNode     *plnode, *result;

            lpipe =
                RxLockedPipeAddFragment(lpipe, NULL, allInOne, instance,
                                        instuserdata, bridge, NULL);

            /* get instance node to generate clusters */
            plnode =
                RxPipelineFindNodeByName(lpipe, instance->name, NULL,
                                         NULL);

            /* We want to upload only position & normal */
            result =
                RxPipelineNodePS2MatInstanceGenerateCluster(plnode,
                                                            &RxClPS2xyz,
                                                            CL_XYZ);
            result =
                RxPipelineNodePS2MatInstanceGenerateCluster(plnode,
                                                            &RxClPS2user1,
                                                            CL_USER1);
            result =
                RxPipelineNodePS2MatInstanceGenerateCluster(plnode,
                                                            &RxClPS2user2,
                                                            CL_USER2);
            result =
                RxPipelineNodePS2MatInstanceGenerateCluster(plnode,
                                                            &RxClPS2user3,
                                                            CL_USER3);

            RxPipelineNodePS2MatInstanceNodeSetVUBufferSizes(plnode,
                                                             pipeASymbStrideOfInputCluster,
                                                             pipeASymbTSVertexCount,
                                                             pipeASymbTLTriCount);

            plnode =
                RxPipelineFindNodeByName(lpipe, bridge->name, NULL,
                                         NULL);
            lpipe = RxLockedPipeUnlock(lpipe);

            /* STEP 2 */
            bridge = RxNodeDefinitionGetPS2MatBridge();
            plnode =
                RxPipelineFindNodeByName(lpipe, bridge->name, NULL,
                                         NULL);
            RxBridgeNodeSetVIFOffset(plnode, pipeASymbVIFOffset);
            plnode =
                RxPipelineFindNodeByName(lpipe, allInOne->name, NULL,
                                         NULL);
            RWASSERT(plnode != NULL);
            result =
                RxPipelineNodePS2ObjAllInOneSetGrouping(plnode, TRUE);


#if (defined(_RWPRTSYSDBG))
            /* Debug info */
            printf("%s(%d): pipeASymbStrideOfInputCluster = %d\n",
                   __FILE__, __LINE__, pipeASymbStrideOfInputCluster);
            printf("%s(%d): pipeASymbTSVertexCount = %d\n", __FILE__,
                   __LINE__, pipeASymbTSVertexCount);
            printf("%s(%d): pipeASymbTLTriCount = %d\n", __FILE__,
                   __LINE__, pipeASymbTLTriCount);
            printf("%s(%d): pipeASymbVIFOffset = %d\n", __FILE__,
                   __LINE__, pipeASymbVIFOffset);
#endif /* (defined(_RWPRTSYSDBG)) */

            if (lpipe == NULL)
            {
                RwDebugSendMessage(rwDEBUGMESSAGE, 
                                   "rpprtsys", 
                                   "Error building pipe");
            }
        }

        /* We now insert our own VU code */
        {
            RxNodeDefinition   *bridge =
                RxNodeDefinitionGetPS2MatBridge();
            RxPipelineNode     *plnode, *result;

            plnode =
                RxPipelineFindNodeByName(lpipe, bridge->name, NULL,
                                         NULL);
            result = RxBridgeNodeSetVU1CodeArray(plnode, vucode);
            if (!result)
            {
                RwDebugSendMessage(rwDEBUGMESSAGE, 
                                   "rpprtsys", 
                                   "Failed to set vucode array");
            }
        }
    }

    RWRETURN(lpipe);
}

#define NUMUSERQUADS (8)

static              RwBool
ParticlesDataNodeBody(RxPipelineNodeInstance * self,
                      const RxPipelineNodeParam * __RWUNUSED__ params)
{

    RxPacket           *packet;
    const RpMesh       *mesh = NULL;
    RxPS2DMASessionRecord *DMASessionRec;
    RpAtomic           *atomic;
    RpParticlesData    *particlesData;
    RwBool              textured;

    RWFUNCTION(RWSTRING("ParticlesDataNodeBody"));

    while ((packet = RxPacketFetch(self)) != NULL)
    {
        RxCluster          *clDMASessionRecord;
        RxCluster          *clMesh;

        clDMASessionRecord =
            RxClusterLockRead(packet, 0 /* RxClPS2DMASessionRecord */ );
        clMesh = RxClusterLockRead(packet, 1 /* RxClPS2Mesh */ );

        RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *) FALSE);
        RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *) TRUE);
        RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *) TRUE);

        /* WARNING!!! 
         * This should really be ...LockWrite, 
         * but that doesn't quite work properly yet 
         */

        DMASessionRec =
            RxClusterGetCursorData(clDMASessionRecord,
                                   RxPS2DMASessionRecord);
        mesh = RxClusterGetCursorData(clMesh, RxPS2Mesh)->mesh;

        textured = (NULL != mesh->material->texture);

        atomic = DMASessionRec->sourceObject.atomic;
        particlesData = *PARTICLESATOMICGETDATA(atomic);

        /* Check to see if we need to reinstance or not */
        /*
         * if ((DMASessionRec->serialNum != 
         ((RwUInt32*)(*(RxClusterGetCursorData(clMesh, 
         RxPS2Mesh)->cacheEntryRef)+1))[3]))
         */
        {
            RwInt32             numIndices;
            RwUInt64            tmpLow, tmpHigh;
            u_long128           tmpTag;
            RwReal              spriteSize;

            sweFinaliseOpenLocalPkt(SWE_LPS_NOFIXUP | 
                                    SWE_PKT_DMA_MODE_CHAIN_TTE | 
                                    SWE_PKT_LOCAL | 
                                    SWE_PKT_VU1 | 
                                    SWE_PKT_CIRCALLOC, -11); /* 10 QW */
            if (!sweLocalPacket)
            {
                RwDebugSendMessage(rwDEBUGMESSAGE, 
                                   "rpprtsys", 
                                   "DMA memory failure");
            }
            
            else
            {
                RwUInt32            prim = 0x0l;

/* HACK */
#ifndef pipeASymbSpriteTypeGIF
#define pipeASymbSpriteTypeGIF    (pipeASymbSpriteTime-1)
#endif /* pipeASymbSpriteTypeGIF */

                /* CNT DMAtag, 
                 * 1 quadword after the dmatag, 
                 * which is our GIFtag */
                tmpLow = ( (1l << 28) | 
                           (NUMUSERQUADS) ) ; 
                /* unpack transfer one quadword */
                tmpHigh = ( (((0x6cl << 24) | 
                              (NUMUSERQUADS << 16) | 
                              ((long) (pipeASymbSpriteTypeGIF))) << 32) | 
                            ((1l << 24) | 
                             (4 << 8) | 
                             (4)) ); /* cycle, wl=4, cl=4 */
                MAKE128(tmpTag, tmpHigh, tmpLow);
                SWEADDCONTFAST(tmpTag); /* QW #1 */

                /* giftag */
                prim = ( /* fix  */ 0x0l << 10 |
                        /* ctxt */ 0x0l << 9 |
                        /* fst  */ 0x0l << 8 |
                        /* aa1  */ 0x0l << 7 |
                        /* antialiasing? */
                        /* abe  */ 0x1l << 6 |
                        /* fge  */ 0x0l << 5 |
                        /* tme  */ textured << 4 |
                        /* iip  */ 0x0l << 3 |
                        /* 0 = flat shading */
                        /* prim */ 0x6l << 0);
                /* Tristrips = 4, Sprites = 6 */

                /* each sprite sets 3/2 per vertex, so 2 verts == 5 regs */
                tmpLow = ( /* regs */ 0x5l << (60 - 32) | 
                          /* flg  */ 0x0l << (58 - 32) |
                          /* prim */ prim << (47 - 32) |
                          /* pre  */ 0x1l << (46 - 32)) << 32;
                tmpHigh = SCE_GS_XYZF2 << 16 | SCE_GS_ST << 12
                    | SCE_GS_XYZF2 << 8 | SCE_GS_RGBAQ << 4 | SCE_GS_ST;
                MAKE128(tmpTag, tmpHigh, tmpLow);
                SWEADDCONTFAST(tmpTag); /* QW #2 */

                /* Time */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->time;
                    pack.floats[1] = particlesData->flightTime;
                    pack.floats[2] = ((RwReal) 0); /* vacancy param to let:) */
                    pack.floats[3] =
                        ((RwReal) 1) / particlesData->flightTime;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #3 */
                }

                /* Particle Size */
                {
                    packL               pack;

                    pack.floats[0] =
                        particlesData->particleSize.y * SPRITESCALE;
                    pack.floats[1] =
                        particlesData->particleSize.x * SPRITESCALE;
                    pack.floats[2] = particlesData->growth;
                    pack.ints[3] = particlesData->numParticles;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #4 */
                }

                /* Particle colorB */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->startCol.red;
                    pack.floats[1] = particlesData->startCol.green;
                    pack.floats[2] = particlesData->startCol.blue;
                    pack.floats[3] = particlesData->startCol.alpha;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #5 */
                }

                /* Particle colorA */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->endCol.red;
                    pack.floats[1] = particlesData->endCol.green;
                    pack.floats[2] = particlesData->endCol.blue;
                    pack.floats[3] = particlesData->endCol.alpha;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #6 */
                }

                /* Emitter3 */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->force.x;
                    pack.floats[1] = particlesData->force.y;
                    pack.floats[2] = particlesData->force.z;
                    pack.floats[3] = ((RwReal) 0);

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #7 */
                }

                /* Emitter2 */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->randomSeed;
                    pack.floats[1] = particlesData->speed;
                    pack.floats[2] = particlesData->speedVariation;
                    pack.floats[3] = particlesData->angle;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #8 */
                }

                /* Emitter1 */
                {
                    packL               pack;

                    pack.floats[0] = particlesData->emitterSize.x;
                    pack.floats[1] = particlesData->emitterSize.y;
                    pack.floats[2] = particlesData->dampening;
                    pack.floats[3] = particlesData->duration;

                    tmpTag = pack.pack;

                    SWEADDCONTFAST(tmpTag); /* QW #9 */
                }

                /* Terminate the DMA with an interrupt */
                tmpLow = (0xfl << 28);
                MAKE128(tmpTag, 0l, tmpLow);
                SWEADDCONTFAST(tmpTag); /* QW #10 */

                sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
            }

            /* Need to copy in the data from
             * whereever it's held into the cluster data */
            numIndices = mesh->numIndices;

            /* Set up the sprite scalars */
            spriteSize = ((RwReal) 120);
        }

        RxPacketDispatch(packet, 0, self);
    }

    RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *) TRUE);

    RWRETURN(TRUE);
}

#define NUMCLUSTERSOFINTEREST \
    ((sizeof(nodeClusters))/(sizeof(nodeClusters[0])))

#define NUMOUTPUTS            \
    ((sizeof(nodeOuts))/(sizeof(nodeOuts[0])))

/*
 * \ingroup rpprtsysskyp2
 * \ref RxNodeDefinitionGetPS2ParticlesData ToDo
 */
RxNodeDefinition   *
RxNodeDefinitionGetPS2ParticlesData(void)
{

    /*******************************************
     **                                       **
     **  PS2MATBRIDGE.CSL NODE SPECIFICATION  **
     **                                       **
     *******************************************/

    static RxClusterRef nodeClusters[] = {
        {&RxClPS2DMASessionRecord, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClPS2Mesh, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClPS2user1, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClPS2user2, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClPS2user3, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClPS2user4, rxCLALLOWABSENT, rxCLRESERVED}
    };

    /* input requirements (this array parallel to ClusterRefs) */
    static RxClusterValidityReq nodeReqs[NUMCLUSTERSOFINTEREST] =
        { rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED
    };

    /* output state (this array parallel to ClusterRefs) */
    static RxClusterValid nodeOut1[NUMCLUSTERSOFINTEREST] =
        { rxCLVALID_VALID,
        rxCLVALID_VALID,
        rxCLVALID_VALID,
        rxCLVALID_VALID,
        rxCLVALID_VALID,
        rxCLVALID_VALID
    };
    static RwChar       _DefaultOutput[] = RWSTRING("DefaultOutput");
    static RxOutputSpec nodeOuts[] =
    { {_DefaultOutput, /* Name */
       nodeOut1, /* OutputClusters */
       rxCLVALID_NOCHANGE /* AllOtherClusters */ }
    };

    static RwChar       _PS2MatBridge_csl[] =
        RWSTRING("PS2UserData.csl");

    static RxNodeDefinition nodePS2UserDataCSL = {
        _PS2MatBridge_csl,      /* Name */
        {                      /* nodemethods */
         ParticlesDataNodeBody, /* +-- nodebody */
         (RxNodeInitFn) NULL,  /* +-- nodeinit */
         (RxNodeTermFn) NULL,  /* +-- nodeterm */
         (RxPipelineNodeInitFn) NULL, /* +-- pipelinenodeinit */
         (RxPipelineNodeTermFn) NULL, /* +-- pipelineNodeTerm */
         (RxPipelineNodeConfigFn) NULL, /* +--  pipelineNodeConfig */
         (RxConfigMsgHandlerFn) NULL /* +--  configMsgHandler */
         },
        {                      /* Io */
         NUMCLUSTERSOFINTEREST, /* +-- NumClustersOfInterest */
         nodeClusters,         /* +-- ClustersOfInterest */
         nodeReqs,             /* +-- InputRequirements */
         NUMOUTPUTS,           /* +-- NumOutputs */
         nodeOuts              /* +-- Outputs */
         },
        0,                     /* PipelineNodePrivateDataSize */
        (RxNodeDefEditable) FALSE, /* editable */
        (RwInt32) 0            /* inPipes */
    };

    /***************************************/

    RxNodeDefinition   *result = &nodePS2UserDataCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPS2ParticlesData"));

    RWRETURN(result);
}

RwBool
_rpParticlesSetupPipes(void)
{
    RWFUNCTION(RWSTRING("_rpParticlesSetupPipes"));

    GParticlesMaterialPipe = RpMaterialGetDefaultPipeline();
    GParticlesObjectPipe = SkyParticlePipeCreate();
    if (!GParticlesObjectPipe)
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

RwBool
_rpParticlesDestroyPipes(void)
{
    RWFUNCTION(RWSTRING("_rpParticlesDestroyPipes"));

    GParticlesMaterialPipe = NULL;

    if (GParticlesObjectPipe)
    {
        RxPipelineDestroy(GParticlesObjectPipe);
        GParticlesObjectPipe = NULL;
    }

    RWRETURN(FALSE);
}

RwBool
_rpParticleExtendGeom(void)
{
    RWFUNCTION(RWSTRING("_rpParticleExtendGeom"));

    /* dummy function */
    RWRETURN(TRUE);
}

RpAtomic           *
_rpParticleAddGeomData(RpAtomic * atomic,
                       RwInt32 __RWUNUSED__ numParticles)
{
    RWFUNCTION(RWSTRING("_rpParticleAddGeomData"));

    /* dummy function */
    RWRETURN(atomic);
}
