#if !defined SPUUTIL_THREADGRP_H__
#include "threadgrp.h"
#endif

#include <stdio.h>
#include <string.h>

namespace spuutil
{
	uint32_t ThreadGrp::Release()
	{
		const uint32_t result = --m_refCount;

		if(  result == 0  )
		{
			this->Reset( 0 );

			delete this;
		}

		return result;
	}


	bool ThreadGrp::SetProgram( uint32_t spuNum, const char* name )
	{

		bool result = false;

		if(  (spuNum < m_spuCount) & ((m_deployedThreads == 0) | (name == 0))  )
		{
			spu_thread_attr_t& attrs = m_attrBlock[spuNum];

			if(  attrs.prog != 0  )
			{
				delete[] attrs.prog;
				attrs.prog = 0;
			}

			bool replacedName = false;

			if(  name  )
			{
				size_t nameLen = strlen( name ) + 1;

				char* programName = new char[nameLen];

				if(  programName  )
				{
					strcpy( programName, name );

					attrs.prog = programName;
					
					replacedName = true;
				}
			}
			
			result = ((replacedName != false) | (name == 0));

            printf( "SetSPUProgram %d %s\n", spuNum, name );

		}

		return result;
	}




	bool ThreadGrp::Deploy()
	{
		bool result = false;

		if(  m_deployedThreads == 0  )
		{
			for(  uint32_t spuIndex = 0; spuIndex < m_spuCount; ++spuIndex  )
			{

				spu_thread_attr_t* const attrs = &m_attrBlock[spuIndex];

				if(  attrs->prog  )
				{
                    printf( "Deploying SPU Thread [spu %d] [pri %d]\n", spuIndex, m_grpPriority );

                    if(  spu_thread_create( &m_threadBlock[spuIndex] ) == SUCCEEDED  )
					{
						int32_t configResult = spu_thread_configure( 	m_threadBlock[spuIndex], 
																		attrs, 
																		m_grpPriority, 
																		m_threadBlock[0], 
																		spuIndex );

						int32_t configedThread = (configResult == SUCCEEDED);
						
						m_deployedThreads |= (configedThread << spuIndex);

                        printf( "SPU Thread %d Deployed : result %d\n", spuIndex, configResult );

					}
						
					m_deployedMask	|= (1 << spuIndex);
				}
			}

			result = (m_deployedThreads == m_deployedMask);
		}

		return result;
	}


	bool ThreadGrp::Start()
	{
		bool result = false;

		if( this->IsFullyDeployed()  )
		{
			result = (spu_thread_start( m_threadBlock[0] ) == SUCCEEDED);

            printf( "Starting SPU ThreadGroup %08x [result %d]\n", this, result );
		}

		return result;
	}

	bool ThreadGrp::Reset( uint32_t spuCount )
	{
		//tear down any existing thread
		for(  uint32_t spuIndex = 0; spuIndex < m_spuCount; ++spuIndex  )
		{
			if(  m_threadBlock[spuIndex] != 0xffffffff  )
			{
				spu_thread_destroy( m_threadBlock[spuIndex] );

				m_threadBlock[spuIndex] = 0xffffffff;

				m_deployedThreads &= ~(1 << spuIndex);
			}
		}


		//handle thread based allocations
		if(  (m_spuCount < spuCount) | (spuCount == 0)  )
		{
			m_attrBlock 	= 0;
			m_threadBlock 	= 0;

			if(  m_privateStore  )
			{
				delete[] m_privateStore;
				m_privateStore = 0;
			}

			if(  spuCount  )
			{
				const uint32_t attrBlockSize 	= (sizeof( spu_thread_attr_t ) * spuCount);
				const uint32_t threadBlockSize	= (sizeof( spu_thread_t ) * spuCount);
				const uint32_t privateStoreSize	= attrBlockSize + threadBlockSize;
				
				m_privateStore = new uint8_t[privateStoreSize];

				m_attrBlock 	= reinterpret_cast< spu_thread_attr_t* >( m_privateStore );
				m_threadBlock	= reinterpret_cast< spu_thread_t* >( m_privateStore + attrBlockSize );

				memset( m_threadBlock, 0xff, threadBlockSize );
			}

			m_maxSPUCount = spuCount;
		}
		


		m_spuCount = spuCount;

		for(  uint32_t spuIndex = 0; spuIndex < m_spuCount; ++spuIndex  )
		{
			m_attrBlock[spuIndex].prog 					= 0;
			m_attrBlock[spuIndex].arg1 					= 0;
			m_attrBlock[spuIndex].arg2 					= 0;
			m_attrBlock[spuIndex].async_intr_enable 	= 0;
		}

		m_deployedMask = 0;

		return true;
	}


	ThreadGrp::ThreadGrp() : 	m_refCount( 1 ),
								m_attrBlock( 0 ),
								m_spuCount( 0 ),
								m_maxSPUCount( 0 ),
								m_grpPriority( 0x80 ),
								m_deployedThreads( 0 ),
								m_deployedMask( 0 ),
								m_privateStore( 0 )
	{
	}
	
	ThreadGrp::ThreadGrp( uint32_t spuCount ) : 	m_refCount( 1 ),
													m_attrBlock( 0 ),
													m_spuCount( 0 ),
													m_maxSPUCount( 0 ),
													m_grpPriority( 0x80 ),
													m_deployedThreads( 0 ),
													m_deployedMask( 0 ),
													m_privateStore( 0 )
	{
		this->Reset( spuCount );
	}
	
	ThreadGrp::~ThreadGrp()
	{

	}
}
