///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

#include <spuutil/pu/threadgrp.h>
#include <spuutil/pu/rawspu.h>
#include <spupages/pu/spupages.h>
#include <stdio.h>

///////////////////////////////////////////////////////////////////////////////

const uint32_t kTargetSPUCount = 4;

spuutil::ThreadGrp *gpSpuThreadGroup;
spupages::SPUPage     TweaksPage;
uint32_t spuCommandQueue[kTargetSPUCount][0x80] __attribute__((aligned(128)));

enum EPageID
{
    EPAGEID_TWEAKS_PAGE = 0,
    EPAGEID_END,
};

///////////////////////////////////////////////////////////////////////////////

bool SetupSPUs( uint32_t targetSPUCount )
{
    const int kMaxSPUs = 6;
    const int kRawSPUs = 1;

    int spuResult = spu_initialize( kMaxSPUs, kRawSPUs );

    printf( "Initialization of %d SPUs, with %d raw %s\n", kMaxSPUs, kRawSPUs, (spuResult == SUCCEEDED) ? "Succeeded" : "Failed" );

    gpSpuThreadGroup = new spuutil::ThreadGrp( targetSPUCount );

    if(  gpSpuThreadGroup  )
    {
        printf( "Created threadgrp with %x threads\n", targetSPUCount );

        TweaksPage.SetELF( "TweaksSpuPage.spu" );

        for(  uint32_t spuTargetIndex = 0; spuTargetIndex < targetSPUCount; ++spuTargetIndex  )
        {
            gpSpuThreadGroup->SetProgram( spuTargetIndex, "SpuPageKernel.spu" );
            gpSpuThreadGroup->SetParam0( spuTargetIndex, (uint32_t)&spuCommandQueue[spuTargetIndex][0] );
        }

        printf( "Deploying threadgrp with %x threads\n", targetSPUCount );

        gpSpuThreadGroup->Deploy();

        int32_t succeeded = gpSpuThreadGroup->IsFullyDeployed();

        if(  succeeded != 0  )
        {
            printf( "Deployed %d threaded page kernel(s)\n", targetSPUCount );

            gpSpuThreadGroup->Start();

            printf( "Started %d threaded page kernel(s)\n", targetSPUCount );

            uint32_t event;
            uint32_t value;

            for(  uint32_t spuTargetIndex = 0; spuTargetIndex < targetSPUCount; ++spuTargetIndex  )
            {
                spu_thread_t spuThreadID = gpSpuThreadGroup->GetThread( spuTargetIndex );

                //sync with, and initialize the page kernel

                spu_thread_get_event( spuThreadID, &event, &value );
                uint64_t cmdQueue = (uint64_t)&spuCommandQueue[spuTargetIndex][0x00];
                const uint32_t eah = (uint32_t)(cmdQueue >> 0x20);
                const uint32_t eal = (uint32_t)(cmdQueue);
                spu_thread_write_spu_mb( spuThreadID, eah );
                spu_thread_write_spu_mb( spuThreadID, eal );
                spu_thread_get_event( spuThreadID, &event, &value );

                //the page kernel is now ready to process commands.

                //start up the WaterPage

                spupages::BuildLoadPageCommand( & spuCommandQueue[spuTargetIndex][0x00], TweaksPage.GetDMABuffer(), TweaksPage.GetDMASize(), EPAGEID_TWEAKS_PAGE, false );
                // spupages_BuildLoadPageCommand( & spuCommandQueue[spuTargetIndex][0x04], waterChunkPage.GetDMABuffer(), waterChunkPage.GetDMASize(), 1, false );
                spupages::ProcessCommandQueue( spuThreadID, 1 );

            }

        }
        else
        {
            gpSpuThreadGroup->Release();
            gpSpuThreadGroup = 0;

            printf( "Failed to fully deploy %d threaded page kernel(s)\n", targetSPUCount );
        }
    }

    return (gpSpuThreadGroup != 0);
}

///////////////////////////////////////////////////////////////////////////////

void SPUExec( void )
{
    int spuIndex = 0;

    static int iframe = 0;

    if( iframe == 500 )
    {
        printf( "SpuExec\n" );

        //spupages_BuildSendEventCommand(     & spuCommandQueue[spuIndex][0x04], 0x0 );

        for( int ispu=0; ispu<4; ispu++ )
        {
            spu_thread_t spuThreadID = gpSpuThreadGroup->GetThread( ispu );

            spupages::BuildExecutePageCommand(   & spuCommandQueue[ispu][0x00], 0, 0, EPAGEID_TWEAKS_PAGE );
            spupages::ProcessCommandQueue( spuThreadID, 1 );

            uint32_t spureadaddr = 0;
            uint32_t spureadval = 0;

            int iret = spu_thread_read_from_ls_32( spuThreadID, 0x00020000, & spureadaddr );

            iret = spu_thread_read_from_ls_32( spuThreadID, spureadaddr, & spureadval );

            printf( "SpuRead [iret %d], [addr %08x] [val %08x]\n", iret, spureadaddr, spureadval );
        }

        iframe = 0;
    }
    iframe++;

}

///////////////////////////////////////////////////////////////////////////////
