#if !defined SPUPAGES_SPUPAGES_H__
#include "spupages.h"
#endif

#if !defined IMAGEINFO_ELF_H__
#include "imageinfo/elf.h"
#endif

#include <stdio.h>

#include <sys/spu_thread.h>

namespace spupages
{

	SPUPage::SPUPage() : m_paramCount( 0 ),
						 m_maxParamCount( 0 ),
						 m_buffer( 0 ),
						 m_dmaBuffer( 0 ),
						 m_dmaBufferSize( 0 ),
						 m_dmaTransferSize( 0 )
	{
		//m_dmaBufferSize = 0x4000;

		//m_buffer = new unsigned char[m_dmaBufferSize + 0x7f];

		//m_dmaBuffer = reinterpret_cast< uint8_t* >( (reinterpret_cast< uint64_t >( (m_buffer + 0x7f) ) & ~0x7f) );	//align the DMA start address
	}

	SPUPage::~SPUPage()
	{
		if(  m_buffer  )
		{
			delete[] m_buffer;
			m_buffer 		= 0;
			m_dmaBuffer 	= 0;
			m_dmaBufferSize	= 0;
		}
	}

	bool SPUPage::SetELF( const char* elfFileName )
	{
		bool result = false;

		FILE* elfFile = fopen( elfFileName, "rb" );

        printf( "SPU ELF Attempting %s\n", elfFileName );

		if(  elfFile  )
		{
            printf( "SPU ELF Loaded %s\n", elfFileName );

			fseek( elfFile, 0, SEEK_END );
			int imageSize = ftell( elfFile );
			fseek( elfFile, 0, SEEK_SET );

            printf( "SPU ELF Size %d\n", imageSize );

            if(  imageSize > m_dmaBufferSize  )
			{
				delete[] m_buffer;

				m_dmaBufferSize = ((imageSize + 0x7f) & ~0x7f);																	//align the transferSize

				m_buffer = new unsigned char[m_dmaBufferSize + 0x7f];

				m_dmaBuffer = reinterpret_cast< uint8_t* >( (reinterpret_cast< uint64_t >( (m_buffer + 0x7f) ) & ~0x7f) );	//align the DMA start address
			}

			if(  m_dmaBuffer  )
			{
				uint32_t alignedImageSize = ((imageSize + 0x7f) & ~0x7f);

				m_dmaTransferSize = (alignedImageSize < m_dmaBufferSize) ? alignedImageSize :m_dmaBufferSize;

				fread( m_dmaBuffer, 1, imageSize, elfFile );

				imageinfo::ELF32Header* elfHeader = (imageinfo::ELF32Header*)&m_dmaBuffer[0x0];
				
				imageinfo::ELF32Phdr* pHeader = (imageinfo::ELF32Phdr*)&m_dmaBuffer[elfHeader->phoff];

				//get the elf's entry point
				*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x00] ) = elfHeader->entry;
				*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x04] ) = 0;
				*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x08] ) = 0;
				*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x0c] ) = 0;

                printf( "SPU ELF EntryPoint %08x\n", elfHeader->entry );

				result = true;
			}
		}

		return result;
	}

	bool SPUPage::SetParam( uint32_t index, uint32_t param )
	{
		bool result = false;

		if(  (index < 2) & (m_dmaBuffer != 0)  )
		{
			uint32_t offset = index << 0x4;

			*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x10 + offset] ) = param;
			*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x14 + offset] ) = 0;
			*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x18 + offset] ) = 0;
			*reinterpret_cast< uint32_t* >( &m_dmaBuffer[0x1c + offset] ) = 0;

			result = true;
		}

		return result;
	}

    void BuildLoadPageCommand( uint32_t* cmdBuffer, const void* ea, uint16_t imageSize, uint8_t pageIndex, bool execute )
    {
	    cmdBuffer[0] = 0x00000000;

	    *(reinterpret_cast< uint64_t* >( &cmdBuffer[1] )) = reinterpret_cast< uint64_t >( ea );

	    cmdBuffer[3] = ((imageSize << 0x10) | pageIndex);
    }


    void BuildExecutePageCommand( uint32_t* cmdBuffer, uint32_t param0, uint32_t param1, uint8_t pageIndex )
    {
	    cmdBuffer[0] = 0x01000000;
	    cmdBuffer[1] = param0;
	    cmdBuffer[2] = param1;
	    cmdBuffer[3] = pageIndex;

    }

    void BuildSetQueueAddressCommand( uint32_t* cmdBuffer, const void* queueAddress )
    {
	    cmdBuffer[0] = 0x02000000;

	    *(reinterpret_cast< uint64_t* >( &cmdBuffer[1] )) = reinterpret_cast< uint64_t >( queueAddress );
    }

    void BuildSendEventCommand( uint32_t* cmdBuffer, uint32_t value )
    {
	    cmdBuffer[0] = 0x03000000;
	    cmdBuffer[1] = value;
	    cmdBuffer[2] = 0;
	    cmdBuffer[3] = 0;
    }

    void ProcessCommandQueue( uint32_t threadID, uint32_t cmdCount )
    {
	    spu_thread_write_signal( threadID, 1, cmdCount );
    }

}
