/*
 * Functionality for 2D rendering
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

/****************************************************************************
 *                                                                          *
 *  Module  :   node2dFillInstance.c                                        *
 *                                                                          *
 *  Purpose :   graphics state                                              *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */

#include <math.h>

#include <rwcore.h>
#include <rpworld.h>

#include <rpdbgerr.h>

#include "rt2d.h"
#include "stroke.h"
#include "brush.h"
#include "gstate.h"

#include "ps2pipes.h"
#include "ps2fill.h"

#include "vufill.h"

/****************************************************************************
 local defines
 */

#define     RT2DFILLPS2QWORD    10


static Rt2dPS2PipeState fillPipeState = {
    (RwUInt64) 0,                    /* subdiv */
    (RxPipeline *)NULL,              /* Pipeline */
    {NULL2D, NULL2D, NULL2D, NULL2D, /* vucode */
     NULL2D, NULL2D, NULL2D, NULL2D,
     NULL2D, NULL2D, NULL2D, NULL2D,
     NULL2D, NULL2D, NULL2D, NULL2D}
};

/*************************************************
 * This is the node body that actually uploads
 * data used by the environment mapping vector
 * code.
 ************************************************/

static              RwBool
PS2FillNode2d(RxPS2Mesh * ps2mesh __RWUNUSED__,
              RxPS2DMASessionRecord * dmaSessionRec __RWUNUSED__,
              RwReal * pPos, RwReal * pNrm)
{
    RwInt32             i;
    RwUInt64            tmp, tmp1;
    u_long128           ltmp = 0;
    rt2dPathNode       *pnode;
    RwReal              colScale;

    RWFUNCTION(RWSTRING("PS2FillNode2d"));

    if (Rt2dGlobals.pnode1 != NULL)
    {
        pnode = Rt2dGlobals.pnode0;

        /* Instance the DMA  data */
        for (i = 0; i < (Rt2dGlobals.numVerts >> 1); i++)
        {
            /* instance the Pos and Normal */
            *pPos++ = pnode->pos.x;
            *pPos++ = pnode->pos.y;

            *pNrm++ = pnode->normal.x;
            *pNrm++ = pnode->normal.y;

            pnode++;
        }

        pnode = Rt2dGlobals.pnode1;

        for (i = 0; i < (Rt2dGlobals.numVerts >> 1); i++)
        {
            /* instance the Pos and Normal */
            *pPos++ = pnode->pos.x;
            *pPos++ = pnode->pos.y;

            *pNrm++ = pnode->normal.x;
            *pNrm++ = pnode->normal.y;

            pnode++;
        }
    }
    else
    {
        pnode = Rt2dGlobals.pnode0;

        /* Instance the DMA  data */
        for (i = 0; i < Rt2dGlobals.numVerts; i++)
        {
            /* instance the Pos and Normal */
            *pPos++ = pnode->pos.x;
            *pPos++ = pnode->pos.y;

            *pNrm++ = pnode->normal.x;
            *pNrm++ = pnode->normal.y;

            pnode++;
        }
    }

    /*************************************************
        * Call a RW3-PS2-specific stealth function to
        * create enough space to store an additional 11
        * quadwords of DMA data.  In fact the RW code
        * will most likely already have enough spare
        * space, since it allocates memory in much bigger
        * chunks.
        * SWE_PKT_LOCAL: RW is responsible for the memory
        *    used for this new packet and will free it
        *    up when the packet has been sent.
        * SWE_PKT_CIRCALLOC: The memory used is taken
        *    from a circularly allocated buffer, which is
        *    much faster than calling regular memory
        *    functions.
        * SWE_PKT_VU1: The packet is destined for the
        *    Vector Unit 1.  (If the SWE_LPS_NOFIXUP
        *    symbol was not used here, then the DMA
        *    manager would try to combine further DMA
        *    packets if they were also destined for the
        *    VU1.)
        ************************************************/

    /* Reserve 10 quadwords for the following data.
     * *
     * * Brush width, inset, oobaseu, layerDepth
     * *
     * * Brush top RGBA
     * * Brush top delta RGBA
     * * Brush bot RGBA
     * * Brush bot delta RGBA
     * *
     * * Brush top UV
     * * Brush top delta UV
     * * Brush bot UV
     * * Brush bot delta UV
     * *
     * * BBox x, y, delta x, delta y
     */
    sweFinaliseOpenLocalPkt(SWE_PKT_DMA_MODE_CHAIN_TTE |
                            SWE_PKT_LOCAL |
                            SWE_PKT_VU1 |
                            SWE_PKT_CIRCALLOC, -(RT2DFILLPS2QWORD + 2));

    /*************************************************
        * If the above call worked then we get a quad-
        * word pointer called sweLocalPacket which points
        * to the memory where we can fill in the DMA
        * data.  The macros below access this pointer and
        * advance this pointer.
        ************************************************/

    if (!sweLocalPacket)
    {
        printf("%s(%d): DMA memory failure\n", __FILE__, __LINE__);
    }
    else
    {

        /*************************************************
            * We build a DMA packet to transfer the necessary
            * data to the VIF.  This is a complete, ready-to
            * go packet, containing commands for the VIF. We
            * send data that must end up in the VU memory,
            * as well as commands that get passed to the GS
            * and program it's registers.
            * We end with an interrupt.
            ************************************************/

        /*************************************************
            * First off we set up a continuous DMA packet
            * to transfer 9 quadwords of data.
            ************************************************/

        tmp = (RT2DFILLPS2QWORD) | /* qwords */
            (1l << 28);        /* ID(cnt) */

        tmp1 =
        /*************************************************
            * We'll be transferring data with no scattering
            ************************************************/
            (VIFCMD_CYCLE | (4l << 8) | ((4))) |
        /*************************************************
            * Write 10 quadwords into VU1 memory
            ************************************************/
            /* UNPACK           qwords      where */
            ((VIFCMD_UNPACK
              | (RT2DFILLPS2QWORD << 16) |
              ((long) (vuFillSymbStaticData))) << 32);

        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        /* width, inset, layerDepth, oobaseu */
        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->halfwidth;
            pack.float32[1] = Rt2dGlobals.pathInset;
            pack.float32[2] = Rt2dGlobals.layerDepth;
            pack.float32[3] = Rt2dGlobals.oobaseu;

            ltmp = pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        /* Colour scale, depending if a texture is present. */
        if (Rt2dGlobals.brush->texture != NULL)
        {
            colScale = RT2DPS2COLSCALE;
        }
        else
        {
            colScale = (RwReal) 1.0;
        }

        /* Brush colour. */
        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->top.col.red * colScale;
            pack.float32[1] = Rt2dGlobals.brush->top.col.green * colScale;
            pack.float32[2] = Rt2dGlobals.brush->top.col.blue * colScale;
            pack.float32[3] = Rt2dGlobals.brush->top.col.alpha * (RwReal) RT2DPS2COLSCALE;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->dtop.col.red * colScale;
            pack.float32[1] = Rt2dGlobals.brush->dtop.col.green * colScale;
            pack.float32[2] = Rt2dGlobals.brush->dtop.col.blue * colScale;
            pack.float32[3] = Rt2dGlobals.brush->dtop.col.alpha * (RwReal) RT2DPS2COLSCALE;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->bottom.col.red * colScale;
            pack.float32[1] = Rt2dGlobals.brush->bottom.col.green * colScale;
            pack.float32[2] = Rt2dGlobals.brush->bottom.col.blue * colScale;
            pack.float32[3] = Rt2dGlobals.brush->bottom.col.alpha * (RwReal) RT2DPS2COLSCALE;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->dbottom.col.red * colScale;
            pack.float32[1] = Rt2dGlobals.brush->dbottom.col.green * colScale;
            pack.float32[2] = Rt2dGlobals.brush->dbottom.col.blue * colScale;
            pack.float32[3] = Rt2dGlobals.brush->dbottom.col.alpha * (RwReal) RT2DPS2COLSCALE;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        /* Brush uv. */
        {
            Rt2dUnion128    pack;

            pack.float32[2] = (RwReal) 0;
            pack.float32[3] = (RwReal) 0;

            pack.float32[0] = Rt2dGlobals.brush->top.uv.x;
            pack.float32[1] = Rt2dGlobals.brush->top.uv.y;
            pack.float32[2] = (RwReal) 0;
            pack.float32[3] = (RwReal) 0;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->dtop.uv.x;
            pack.float32[1] = Rt2dGlobals.brush->dtop.uv.y;
            pack.float32[2] = (RwReal) 0;
            pack.float32[3] = (RwReal) 0;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->bottom.uv.x;
            pack.float32[1] = Rt2dGlobals.brush->bottom.uv.y;
            pack.float32[2] = (RwReal) 0;
            pack.float32[3] = (RwReal) 0;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.brush->dbottom.uv.x;
            pack.float32[1] = Rt2dGlobals.brush->dbottom.uv.y;
            pack.float32[2] = (RwReal) 0;
            pack.float32[3] = (RwReal) 0;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        /* bbox.xy, delta.xy */
        {
            Rt2dUnion128    pack;

            pack.float32[0] = Rt2dGlobals.bbox.x;
            pack.float32[1] = Rt2dGlobals.bbox.y;
            pack.float32[2] = Rt2dGlobals.delta.x;
            pack.float32[3] = Rt2dGlobals.delta.y;

            ltmp =  pack.int128;
            SWEADDCONTFAST(ltmp);
        }

        /*    End with intr */
        tmp = (0xfl << 28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);

    }

    RWRETURN(TRUE);
}

static              RwBool
rxNode2dPS2FillPS2ManagerInstanceCallBack(void **clusterData,
                                          RwUInt32 numClusters
                                          __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("rxNode2dPS2FillPS2ManagerInstanceCallBack"));

    RWRETURN(PS2FillNode2d((RxPS2Mesh *) (clusterData[0]),
                           (RxPS2DMASessionRecord *) (clusterData[1]),
                           (RwReal *) (clusterData[2]), /* Fill Pos */
                           (RwReal *) (clusterData[3]) /* File Nrm */
             ));
}

RxPipeline         *
_rt2dPS2FillPipe(void)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("_rt2dPS2FillPipe"));

    pipe = (RxPipeline *)NULL;

    /* Fill Pipe. */
    pipe = RxPipelineCreate();

    if (pipe)
    {
        RwInt32             i;
        RxLockedPipe       *lpipe = (RxPipeline *)NULL;

        lpipe = RxPipelineLock(pipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *ps2man;
            RxPipelineNode     *plnode, *result;

            ps2man = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_IM3D);

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

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

            /* Set up the first to carry x, y */
            Rt2dGlobals.RxClPS2FillPos = RxClPS2user1;
            Rt2dGlobals.RxClPS2FillPos.defaultAttributes &= ~CL_V4_32;
            Rt2dGlobals.RxClPS2FillPos.defaultAttributes |= CL_V2_32;

            result =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &Rt2dGlobals.
                                                        RxClPS2FillPos,
                                                        CL_USER1);

            /* Set up the second to carry ny, ny */
            Rt2dGlobals.RxClPS2FillNrm = RxClPS2user2;
            Rt2dGlobals.RxClPS2FillNrm.defaultAttributes &= ~CL_V4_32;
            Rt2dGlobals.RxClPS2FillNrm.defaultAttributes |= CL_V2_32;

            result =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &Rt2dGlobals.
                                                        RxClPS2FillNrm,
                                                        CL_USER2);

            RWASSERT(result != NULL);

            /* This is irrelevant for now but we do it anyway */
            RxPipelineNodePS2ManagerSetVUBufferSizes(plnode, /* Node */
                                                     vuFillSymbStrideOfInputCluster, /* Stride of input cluster */
                                                     vuFillSymbTSVertexCount, /* Tristrip vertex count */
                                                     vuFillSymbTLTriCount); /* Trilist vertex count */

            lpipe = RxLockedPipeUnlock(lpipe);
        }

        if (NULL != lpipe)
        {
            RxNodeDefinition   *ps2man;
            RxPipelineNode     *plnode, *result;

            ps2man = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_IM3D);
            plnode = RxPipelineFindNodeByName(lpipe,
                                              ps2man->name,
                                              (RxPipelineNode *)NULL,
                                              (RwInt32 *)NULL);

            RWASSERT(plnode != NULL);

            /* We now insert our own instance callback */
            result = RxPipelineNodePS2ManagerSetInstanceCallBack(plnode,
                                                                 rxNode2dPS2FillPS2ManagerInstanceCallBack);

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

            /* Set the transforms/renderers */
            /* pipeState.vucode[TRANSNFOG | TRANSNCL | TRANSSTRIP | TRANSPER] = &font2d;  */

            /* We now insert our own VU code */
            result =
                RxPipelineNodePS2ManagerSetVU1CodeArray(plnode,
                                                        fillPipeState.vucode);

            Rt2dGlobals.default_fill_pipe = pipe;
            Rt2dGlobals.use_fill_pipe = pipe;
        }
    }

    RWRETURN(pipe);
}

Rt2dPath           *
_rt2dPS2PathFill(Rt2dPath * path, Rt2dBrush * brush)
{
    Rt2dPath           *flat;
    rt2dPathNode       *pnode0, *pnode1;
    RwInt32             vcount_t, vcount;
    RWIM3DVERTEX       *vdst;
    RwReal              oobaseu, halfwidth, layerDepth;

    RWFUNCTION(RWSTRING("_rt2dPS2PathFill"));

    /* NULL path is valid */
    if (path)
    {
        RwMatrix           *ctm = _rt2dCTMGet();
        Rt2dBBox            bbox;
        rwIm3DPool         *pool;

        if (path->closed)
        {
            layerDepth = Rt2dGlobals.layerDepth;
            halfwidth = brush->halfwidth;

            /* we'll be using the flattened path */
            flat = path;
            if (!path->flat)
            {
                flat = _rt2dScratchPath();
                _rt2dSubPathFlatten(flat, path);
            }

            pnode0 = (rt2dPathNode *) rwSListGetArray(flat->segments);
            vcount_t = rwSListGetNumEntries(flat->segments) - 1;

            pnode1 = &pnode0[vcount_t];

            if (brush->texture)
            {
                const RwTextureFilterMode filterMode =
                    RwTextureGetFilterMode(brush->texture);
                const RwTextureAddressMode addressingMode =
                    RwTextureGetAddressing(brush->texture);

                RwRenderStateSet(rwRENDERSTATETEXTURERASTER,
                                 (void *)
                                 RwTextureGetRaster(brush->texture));
                RwRenderStateSet(rwRENDERSTATETEXTUREFILTER,
                                 (void *) filterMode);
                RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS,
                                 (void *) addressingMode);
            }
            else
            {
                RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
            }

            /* we need the bbox to generate vertex shading info */
            bbox.x = bbox.w = pnode0[0].pos.x;
            bbox.y = bbox.h = pnode0[0].pos.y;

            /* get left/right NOT pos/width */
            _rt2dFlatSubPathExtendBBox(flat, &bbox);
            Rt2dGlobals.delta.x = ((RwReal) 1.0) / (bbox.w - bbox.x);
            Rt2dGlobals.delta.y = ((RwReal) 1.0) / (bbox.h - bbox.y);

            Rt2dGlobals.bbox.x = bbox.x;
            Rt2dGlobals.bbox.y = bbox.y;

            vdst = brush->vertex;

            Rt2dGlobals.pathInset = path->inset;
            Rt2dGlobals.brush = brush;

            oobaseu = 1;

            Rt2dGlobals.pathInset = path->inset;
            Rt2dGlobals.brush = brush;
            Rt2dGlobals.oobaseu = oobaseu;

            pool = _rwIm3DGetPool();

            vcount = vcount_t;

            while (vcount_t > 0)
            {
                if (vcount_t > RT2DPS2MAXVERT)
                {
                    Rt2dGlobals.numVerts = RT2DPS2MAXVERT;

                    Rt2dGlobals.pnode0 = pnode0;
                    Rt2dGlobals.pnode1 = (pnode1 -=
                                          (RT2DPS2MAXVERT >> 1));

                    vcount_t -= (RT2DPS2MAXVERT - 2);
                    pnode0 += ((RT2DPS2MAXVERT >> 1) - 1);
                    pnode1 -= ((RT2DPS2MAXVERT >> 1) - 1);
                }
                else
                {
                    Rt2dGlobals.numVerts = vcount_t;

                    Rt2dGlobals.pnode0 = pnode0;
                    Rt2dGlobals.pnode1 = (rt2dPathNode *)NULL;

                    vcount_t = 0;
                }

                pool->numElements = (RwUInt16) Rt2dGlobals.numVerts;
                pool->stash.numIndices =
                    (RwUInt16) Rt2dGlobals.numVerts;

                pool->elements = brush->vertex;
                pool->stride = 20; /* 5 x 4 */

                pool->stash.ltm = ctm;
                pool->stash.flags = Rt2dGlobals.TransformFlags;

                pool->stash.pipeline = (RxPipeline *) NULL;
                pool->stash.primType =
                    (RwPrimitiveType) rwPRIMTYPETRISTRIP;
                pool->stash.indices = (RxVertexIndex *) NULL;

                if (RxPipelineExecute
                    (Rt2dGlobals.use_fill_pipe, (void *) &pool->stash,
                     FALSE) == NULL)
                {
                    RWRETURN((Rt2dPath *)NULL);
                }
            }
        }

        _rt2dPS2PathFill(path->next, brush);
    }

    RWRETURN(path);
}
