//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_SPURS_TaskController.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "PPU_SPU_Utils/Asura_PS3_SPURS_TaskController.h"

// Stdlib platform-specific.
#include <stdlib.h>
#include <stdio.h>

// Engine independent.
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Assert.h"

// Engine platform-specific.
#include "PPU_SPU_Utils/Asura_PS3_SPURS.h"


Asura_PS3_SPURS_TaskController::Asura_PS3_SPURS_TaskController( u_int uNumSPUs, const char* szGroupName, const void* pvELFAddr, u_int uQueueEntrySize, u_int uMaxQueueEntries )
{
	if ( Asura_PS3_SPURS::IsInitialised() == false )
	{
		Asura_PS3_SPURS::Initialise( uNumSPUs );
	}

    m_pQueueBuffer = Asura_AllocateAlign( uQueueEntrySize * ( uMaxQueueEntries + Asura_PS3_SPURS::m_uNumber_Of_SPUs_To_Use ), ASURA_ALIGN_16_BYTE );
    ASURA_STATIC_ASSERT( CELL_SYNC_LFQUEUE_ALIGN == ASURA_ALIGN_128_BYTE );
    
	m_pxQueueEA = static_cast<CellSpursLFQueue*>( Asura_AllocateAlign(CELL_SYNC_LFQUEUE_SIZE, ASURA_ALIGN_128_BYTE) );
    Asura_Mem::Set( m_pxQueueEA, 0, CELL_SYNC_LFQUEUE_SIZE );

	m_pxTaskset = new Asura_PS3_SPURS::Asura_PS3_SPURS_Taskset( &m_xSPURS, szGroupName, (uint64_t)m_pxQueueEA );

	int iRetCode	= cellSpursLFQueueInitialize( m_pxTaskset->GetTaskset(), m_pxQueueEA, m_pQueueBuffer, uQueueEntrySize, uMaxQueueEntries, CELL_SYNC_QUEUE_PPU2SPU );
	iRetCode		= cellSpursLFQueueAttachLv2EventQueue( m_pxQueueEA );
    Asura_Assert( CELL_OK == iRetCode, "Failed to initialise queue" );

	m_pvELFAddr			= pvELFAddr;
	m_uNumTasks			= uNumSPUs;
	m_bQueueTerminated	= false;
}

Asura_PS3_SPURS_TaskController::~Asura_PS3_SPURS_TaskController()
{
	m_pxTaskset->End( &m_xSPURS );

	delete m_pxTaskset;
	m_pxTaskset = NULL;

	Asura_DeallocateAlign( m_pxQueueEA );
	m_pxQueueEA = NULL;

	Asura_DeallocateAlign( m_pQueueBuffer );
	m_pQueueBuffer = NULL;
}

void Asura_PS3_SPURS_TaskController::Begin( void* pvTaskArg )
{
	for ( u_int k = 0 ; k < m_uNumTasks ; k++ )
	{
		m_pxTasks[k] = m_pxTaskset->Begin( &m_xSPURS, m_pvELFAddr, pvTaskArg );
	}
}

void Asura_PS3_SPURS_TaskController::Begin( void* pvTaskArg, void* pContext )
{
	for ( u_int k = 0 ; k < m_uNumTasks ; k++ )
	{
		m_pxTasks[k] = m_pxTaskset->Begin( &m_xSPURS, m_pvELFAddr, pvTaskArg, pContext );
	}
}

void Asura_PS3_SPURS_TaskController::End( void )
{
	if ( m_bQueueTerminated == false )
	{
		PushTerminators();
	}

	for ( u_int k = 0 ; k < m_uNumTasks ; k++ )
	{
		int i = CELL_OK;
		do 
		{
			i = Asura_PS3_SPURS::NonBlockingWaitForTask( m_pxTasks[k] );
		} while ( i != CELL_OK );

		
	}
	for ( u_int k = 0 ; k < m_uNumTasks ; k++ )
	{
		Asura_PS3_SPURS::ShutdownTask( m_pxTasks[k] );
	}
	m_bQueueTerminated = false;

	u_int uQueueSize = 0;
	u_int uResult = 0;
	uResult = cellSpursLFQueueSize( GetQueue(), &uQueueSize );
	if ( uQueueSize > 0 )
	{
		Asura_Assert( CELL_OK == uResult && !uQueueSize, "Asura_PS3_SPURS_TaskController::End - Queue not empty after SPUs finished?" );
		cellSpursLFQueueClear( GetQueue() );
	}

}

int	Asura_PS3_SPURS_TaskController::PushTask_Blocking( void* pBuffer )
{
	int	iResult = CELL_OK;
	do
	{
	} while ( (iResult = cellSpursLFQueueTryPush( m_pxQueueEA, pBuffer )) != CELL_OK );
	return CELL_OK;
}

int	Asura_PS3_SPURS_TaskController::PushTask( void* pBuffer )
{
	int uResult = cellSpursLFQueueTryPush( m_pxQueueEA, pBuffer );
	return uResult;
}

void	Asura_PS3_SPURS_TaskController::PushTerminators( void )
{
	//if ( m_bQueueTerminated == false )
	{
		for ( u_int k = 0 ; k < m_uNumTasks ; k++ )
		{
			u_int	uResult = CELL_OK;
			do 
			{
			} while( (uResult	= cellSpursLFQueueTryPush( m_pxQueueEA, m_pvTerminatorJob )) != 0 );
		}
	}
	m_bQueueTerminated = true;
}

int	Asura_PS3_SPURS_TaskController::PopTask_Blocking( void* pBuffer )
{
	int	iResult = CELL_OK;
	do
	{
	} while ( (iResult = cellSpursLFQueueTryPop( m_pxQueueEA, pBuffer )) != CELL_OK );
	return CELL_OK;
}

int	Asura_PS3_SPURS_TaskController::PopTask( void* pBuffer )
{
	int uResult = cellSpursLFQueueTryPop( m_pxQueueEA, pBuffer );
	return uResult;
}

u_int	Asura_PS3_SPURS_TaskController::GetNumEntriesInQueue( void )
{
	u_int uQueueSize = 0;
	cellSpursLFQueueSize( GetQueue(), &uQueueSize );
	return uQueueSize;
}

int		Asura_PS3_SPURS_TaskController::ClearQueue( void )
{
	int	uResult = cellSpursLFQueueClear( GetQueue() );
	return uResult;
}