#include "stdafx.h"
#include <libsysmodule.h>
#include <rudp.h>
#include <audioin.h>
#include <audioout.h>
#include <fios2.h>

#include "SonyVoiceChat_Orbis.h"

std::vector<SQRVoiceConnection*>	SonyVoiceChat_Orbis::m_remoteConnections;
bool								SonyVoiceChat_Orbis::m_bVoiceStarted = false;
int									SonyVoiceChat_Orbis::m_numLocalDevicesConnected = 0; 
SQRLocalVoiceDevice					SonyVoiceChat_Orbis::m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT];
uint32_t							SonyVoiceChat_Orbis::m_voiceOutPort;
bool								SonyVoiceChat_Orbis::m_forceSendPacket = false; // force a packet across the network, even if there's no data, so we can update flags
RingBuffer							SonyVoiceChat_Orbis::m_recordRingBuffer(sc_ringBufferSize);
VoicePacket::Flags					SonyVoiceChat_Orbis::m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT];
bool								SonyVoiceChat_Orbis::m_bInitialised = false;
CRITICAL_SECTION					SonyVoiceChat_Orbis::m_csRemoteConnections;

// sample related variables
SceVoiceStartParam  startParam;
int32_t		 playSize = 0;

static const int sc_thresholdValue = 100;

static const bool sc_verbose = false;

// #define _USE_PCM_AUDIO_
//#define USE_PCM_MIC_DATA



int g_loadedPCMVoiceDataSizes[4];
int g_loadedPCMVoiceDataPos[4];
char* g_loadedPCMVoiceData[4];

static void CreatePort(uint32_t *portId, const SceVoicePortParam *pArg)
{
	C4JThread::PushAffinityAllCores();	// PS4 only

	int err = sceVoiceCreatePort( portId, pArg );
	assert(err == SCE_OK);
	assert(*portId != SCE_VOICE_INVALID_PORT_ID);
	C4JThread::PopAffinity();	// PS4 only
}

static void DeletePort(uint32_t& port)
{
	int32_t result;
	if (port != SCE_VOICE_INVALID_PORT_ID)
	{
		result = sceVoiceDeletePort( port );
		if (result != SCE_OK)
		{
			app.DebugPrintf("sceVoiceDeletePort failed %0x\n", result);
			assert(0);
		}
		port = SCE_VOICE_INVALID_PORT_ID;
	}
}


void LoadPCMVoiceData()
{
	for(int i=0;i<4;i++)
	{
		char filename[64];
		sprintf(filename, "voice%d.pcm", i+1);
		HANDLE file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		DWORD dwHigh=0;
		g_loadedPCMVoiceDataSizes[i] = GetFileSize(file,&dwHigh);

		if(g_loadedPCMVoiceDataSizes[i]!=0)
		{
			g_loadedPCMVoiceData[i] = new char[g_loadedPCMVoiceDataSizes[i]];
			DWORD bytesRead;
			BOOL bSuccess = ReadFile(file, g_loadedPCMVoiceData[i], g_loadedPCMVoiceDataSizes[i], &bytesRead, NULL);
			assert(bSuccess);
		}
		g_loadedPCMVoiceDataPos[i] = 0;
	}
}


void SonyVoiceChat_Orbis::init()
{
	int returnCode = SCE_OK;
	SceUserServiceUserId initialUserId;

	if (returnCode < 0) 
	{
		app.DebugPrintf("Error: sceSysmoduleLoadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode);
		assert(0);
	}


	SceVoiceInitParam           params;
	SceVoicePortParam           portArgs;
	memset( &params, 0, sizeof(params) );
	params.appType = SCE_VOICE_APPTYPE_GAME;
	params.onEvent = 0;
	returnCode = sceVoiceInit( &params , SCE_VOICE_VERSION_100);
	if (returnCode < 0) 
	{
		app.DebugPrintf("Error: sceVoiceInit(), ret 0x%08x\n", returnCode);
		assert(0);
	}

#ifdef _USE_PCM_AUDIO_
	portArgs.portType                  = SCE_VOICE_PORTTYPE_OUT_PCMAUDIO;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 0;
	portArgs.volume                    = 1.0f;
	portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
	portArgs.pcmaudio.format.sampleRate = SCE_VOICE_SAMPLINGRATE_16000;
	portArgs.pcmaudio.bufSize			= 4096;
#else
	portArgs.portType                  = SCE_VOICE_PORTTYPE_OUT_VOICE;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 0;
	portArgs.volume                    = 1.0f;
	portArgs.voice.bitrate             = VOICE_ENCODED_FORMAT;
#endif
	CreatePort( &m_voiceOutPort, &portArgs );

	start();
	m_bInitialised = true;
	InitializeCriticalSection(&m_csRemoteConnections);
}


void SonyVoiceChat_Orbis::start()
{
	if( m_bVoiceStarted == false)
	{
		startParam.container = malloc(SCE_VOICE_MEMORY_CONTAINER_SIZE);
		startParam.memSize = SCE_VOICE_MEMORY_CONTAINER_SIZE;

		int err;

		C4JThread::PushAffinityAllCores(); // PS4 only
		err = sceVoiceStart(&startParam);
		assert(err == SCE_OK);
		C4JThread::PopAffinity();	// PS4 only

		m_bVoiceStarted = true;
	}
}

void SonyVoiceChat_Orbis::checkFinished()
{
	EnterCriticalSection(&m_csRemoteConnections);

	for(int i=0;i<m_remoteConnections.size();i++)
	{
	 	assert(m_remoteConnections[i]->m_bFlaggedForShutdown);
	}
// 	assert(m_numLocalDevicesConnected == 0);

	LeaveCriticalSection(&m_csRemoteConnections);
}


void SonyVoiceChat_Orbis::shutdown()
{
	m_bInitialised = false;
	int32_t result;
	result = sceVoiceStop();
	assert(result == SCE_OK);
	result = sceVoiceEnd();
	assert(result == SCE_OK);
	free(startParam.container);

	int returnCode = sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE);
	if (returnCode < 0) 
	{
		app.DebugPrintf("Error: sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode);
		assert(0);
	}
}

void SonyVoiceChat_Orbis::setEnabled( bool bEnabled )
{
}



// Internal send function. This attempts to send as many elements in the queue as possible until the write function tells us that we can't send any more. This way,
// we are guaranteed that if there *is* anything more in the queue left to send, we'll get a CELL_RUDP_CONTEXT_EVENT_WRITABLE event when whatever we've managed to
// send here is complete, and can continue on.
void SQRVoiceConnection::SendMoreInternal()
{
	EnterCriticalSection(&m_csQueue);
	bool keepSending;
	do
	{
		keepSending = false;
		if( m_sendQueue.size() > 0)
		{
			// Attempt to send the full data in the first element in our queue
			unsigned char *data= m_sendQueue.front().current;
			int dataSize = m_sendQueue.front().end - m_sendQueue.front().current;
			int ret = sceRudpWrite( m_rudpCtx, data, dataSize, 0);//CELL_RUDP_MSG_LATENCY_CRITICAL  );
			int wouldBlockFlag =  SCE_RUDP_ERROR_WOULDBLOCK;

			if( ret == dataSize )
			{
				// Fully sent, remove from queue - will loop in the while loop to see if there's anything else in the queue we could send
				delete [] m_sendQueue.front().start;
				m_sendQueue.pop();
				if( m_sendQueue.size() )
				{
					keepSending = true;
				}
			} 
			else if( ( ret >= 0 ) || ( ret == wouldBlockFlag ) )
			{


				// Things left to send - adjust this element in the queue
				int	remainingBytes;
				if( ret >= 0 )
				{
					// Only ret bytes sent so far
					remainingBytes = dataSize - ret;
					assert(remainingBytes > 0 );
				}
				else
				{
					// Is CELL_RUDP_ERROR_WOULDBLOCK, nothing has yet been sent
					remainingBytes = dataSize;
				}
				m_sendQueue.front().current = m_sendQueue.front().end - remainingBytes;	
			}
		}
	} while (keepSending);
	LeaveCriticalSection(&m_csQueue);
}

void SQRVoiceConnection::SendInternal(const void *data, unsigned int dataSize)
{
 	EnterCriticalSection(&m_csQueue);

	QueuedSendBlock sendBlock;

	unsigned char *dataCurrent = (unsigned char *)data;
	unsigned int dataRemaining = dataSize;

	while( dataRemaining )
	{
		int dataSize = dataRemaining;
		if( dataSize > SNP_MAX_PAYLOAD ) dataSize = SNP_MAX_PAYLOAD;
		sendBlock.start = new unsigned char [dataSize];
		sendBlock.end = sendBlock.start + dataSize;
		sendBlock.current = sendBlock.start;
		memcpy( sendBlock.start, dataCurrent, dataSize);
		m_sendQueue.push(sendBlock);		
		dataRemaining -= dataSize;
		dataCurrent += dataSize;
	}

//	app.DebugPrintf("voice sent %d bytes\n", dataSize);

	// Now try and send as much as we can
	SendMoreInternal();

 	LeaveCriticalSection(&m_csQueue);
}

void SQRVoiceConnection::readRemoteData()
{
	unsigned int dataSize = sceRudpGetSizeReadable(m_rudpCtx);
	if( dataSize > 0 )
	{
		VoicePacket packet;
		int bytesRead = sceRudpRead( m_rudpCtx, &packet, dataSize, 0, NULL );
		unsigned int writeSize;
		if( bytesRead > 0 )
		{
//			app.DebugPrintf("voice received %d bytes\n", bytesRead);
			writeSize = bytesRead;
			packet.verifyData(bytesRead, 19);
			addPacket(packet);
// 			m_playRingBuffer.Write((char*)data, writeSize);

		}
	}

}



SQRVoiceConnection::SQRVoiceConnection( int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId ) 
	: m_rudpCtx(rudpCtx)
	, m_remoteRoomMemberId(remoteRoomMemberId)
	, m_bConnected(false)
	, m_headsetConnectionMask(0)
	, m_playRingBuffer(sc_ringBufferSize)
{
	InitializeCriticalSection(&m_csQueue);
	InitializeCriticalSection(&m_csPacketQueue);

	SceVoiceInitParam           params;
	SceVoicePortParam           portArgs;
#ifdef _USE_PCM_AUDIO_
	portArgs.portType                  = SCE_VOICE_PORTTYPE_IN_PCMAUDIO;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 100;
	portArgs.volume                    = 1.0f;
	portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000;
	portArgs.pcmaudio.format.dataType  = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
	portArgs.pcmaudio.bufSize          = 4096;
#else
	portArgs.portType                  = SCE_VOICE_PORTTYPE_IN_VOICE;
	portArgs.bMute                     = false;
	portArgs.threshold                 = sc_thresholdValue;     // compensate network jitter
	portArgs.volume                    = 1.0f;
	portArgs.voice.bitrate             = VOICE_ENCODED_FORMAT;
#endif
	CreatePort( &m_voiceInPort, &portArgs );
	m_nextExpectedFrameIndex = 0;
	m_bFlaggedForShutdown = false;
}

SQRVoiceConnection::~SQRVoiceConnection()
{
	DeleteCriticalSection(&m_csQueue);
	DeleteCriticalSection(&m_csPacketQueue);
	extern int g_numRUDPContextsBound;
	int err = sceRudpTerminate( m_rudpCtx );
	app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" );

	app.DebugPrintf("-----------------------------\n");
	if(err<0)
		app.DebugPrintf("Voice rudp context failed to delete!!! %d\n", m_rudpCtx);
	else
	{
		g_numRUDPContextsBound--;
		app.DebugPrintf("Voice rudp context deleted %d\n", m_rudpCtx);
	}
	app.DebugPrintf("-----------------------------\n");

	DeletePort(m_voiceInPort);

}

bool SQRVoiceConnection::getNextPacket( VoicePacket& packet )
{
	EnterCriticalSection(&m_csPacketQueue); 
	bool retVal = false;
	if(m_receivedVoicePackets.size() > 0)
	{
		retVal = true;
		packet = m_receivedVoicePackets.front();
		m_receivedVoicePackets.pop();
	}
	LeaveCriticalSection(&m_csPacketQueue);
	return retVal;
}

void SQRVoiceConnection::addPacket( VoicePacket& packet )
{
	EnterCriticalSection(&m_csPacketQueue);
	m_receivedVoicePackets.push(packet);
	LeaveCriticalSection(&m_csPacketQueue);
}

int g_frameNum = 0;
bool g_bRecording = false;


uint32_t					frameSendIndex = 0;
uint32_t					lastReadFrameCnt = 0;


void PrintAllOutputVoiceStates( std::vector<SQRVoiceConnection*>& connections)
{
	for(int rIdx=0;rIdx<connections.size(); rIdx++)
	{
		SQRVoiceConnection* pVoice = connections[rIdx];
		SceVoiceBasePortInfo        portInfo;
		int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo );
		static SceVoicePortState lastPortState = SCE_VOICE_PORTSTATE_IDLE;
		if(portInfo.state != lastPortState)
		{
			lastPortState = portInfo.state;
			switch(portInfo.state)
			{
			case SCE_VOICE_PORTSTATE_IDLE:
				app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_IDLE\n");
				break;
			case SCE_VOICE_PORTSTATE_BUFFERING:
				app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_BUFFERING\n");
				break;
			case SCE_VOICE_PORTSTATE_RUNNING:
				app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_RUNNING\n");
				break;
			case SCE_VOICE_PORTSTATE_READY:
				app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_READY\n");
				break;
			case SCE_VOICE_PORTSTATE_NULL:
			default:
				app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_NULL\n");
				break;
			}
		}
	}

}


void SonyVoiceChat_Orbis::sendPCMMicData()
{
	int32_t                     result;
	uint32_t                    outputPortBytes;
	VoicePacket					packetToSend;
	uint32_t					readSize;
	SceVoiceBasePortInfo        portInfo;
	memset( &portInfo, 0, sizeof(portInfo) );
	uint16_t					frameGap = 0;

	DWORD tick = GetTickCount();
	static DWORD lastTick = 0;
	int numFrames = ceilf((tick - lastTick)/16.0f);
	lastTick = tick;
	readSize = 512 * numFrames;

	if(g_loadedPCMVoiceDataPos[0] + readSize < g_loadedPCMVoiceDataSizes[0])
	{
		for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
		{
			if(m_localVoiceDevices[i].isValid())
			{
				result = sceVoiceWriteToIPort(m_localVoiceDevices[i].m_microphonePort, &g_loadedPCMVoiceData[0][g_loadedPCMVoiceDataPos[0]], &readSize, 0);
			}
		}
	}
	g_loadedPCMVoiceDataPos[0] += readSize;
	if(g_loadedPCMVoiceDataPos[0] > (g_loadedPCMVoiceDataSizes[0] + 8192))
		g_loadedPCMVoiceDataPos[0] = 0;


}

void SonyVoiceChat_Orbis::sendAllVoiceData()
{
	int32_t                     result;
	uint32_t                    outputPortBytes;
	VoicePacket					packetToSend;
	uint32_t					readSize;
	SceVoiceBasePortInfo        portInfo;
	memset( &portInfo, 0, sizeof(portInfo) );
	uint16_t					frameGap = 0;

	VoicePacket::Flags lastPlayerFlags[MAX_LOCAL_PLAYER_COUNT];

	for(int i=0; i<MAX_LOCAL_PLAYER_COUNT;i++)
		lastPlayerFlags[i] = m_localPlayerFlags[i];

	bool flagsChanged = false;


	
	// grab the status of all the local voices
	for(int i=0; i<MAX_LOCAL_PLAYER_COUNT;i++)
	{
		if(m_localVoiceDevices[i].isValid())
		{
			bool bChatRestricted = false;
			ProfileManager.GetChatAndContentRestrictions(i,true,&bChatRestricted,NULL,NULL);

			if(bChatRestricted)
			{
				m_localPlayerFlags[i].m_bHasMicConnected = false;
			}
			else
			{
				/* Obtain port state */
				int32_t state;
				int err = sceVoiceGetPortAttr(m_localVoiceDevices[i].m_microphonePort, SCE_VOICE_ATTR_AUDIOINPUT_SILENT_STATE, &state,sizeof(state));
				if(err == SCE_OK)
				{
					if (state == 0) 
						m_localPlayerFlags[i].m_bHasMicConnected = true;
					else
						m_localPlayerFlags[i].m_bHasMicConnected = false;
				}
				else
					m_localPlayerFlags[i].m_bHasMicConnected = false;

			}
			
			if(m_localPlayerFlags[i].m_bHasMicConnected)
			{
				SceVoiceBasePortInfo        portInfo;
				int result = sceVoiceGetPortInfo(m_localVoiceDevices[i].m_microphonePort, &portInfo );
				assert(result == SCE_OK);
				switch(portInfo.state)
				{
				case SCE_VOICE_PORTSTATE_READY:
				case SCE_VOICE_PORTSTATE_BUFFERING:
				case SCE_VOICE_PORTSTATE_IDLE:
					m_localPlayerFlags[i].m_bTalking = false;
					break;
				case SCE_VOICE_PORTSTATE_RUNNING:
					m_localPlayerFlags[i].m_bTalking = true;
					break;
				default:
					assert(0);
				}
			}
			else
			{
				m_localPlayerFlags[i].m_bTalking = false;
			}
		}
		else
		{
			m_localPlayerFlags[i].m_bHasMicConnected = false;
			m_localPlayerFlags[i].m_bTalking = false;
		}		
		packetToSend.m_localPlayerFlags[i] = m_localPlayerFlags[i];
		if(m_localPlayerFlags[i].m_bHasMicConnected != lastPlayerFlags[i].m_bHasMicConnected ||
			m_localPlayerFlags[i].m_bTalking != lastPlayerFlags[i].m_bTalking)
			flagsChanged = true;
	}


	if(sc_verbose)
	{
		EnterCriticalSection(&m_csRemoteConnections);
		PrintAllOutputVoiceStates(m_remoteConnections);
		LeaveCriticalSection(&m_csRemoteConnections);
	}
	result = sceVoiceGetPortInfo(m_voiceOutPort, &portInfo );
	if (result != SCE_OK)
	{
		app.DebugPrintf("sceVoiceGetPortInfo failed %x\n", result);
		assert(0);
	}




	outputPortBytes = portInfo.numByte;
	outputPortBytes = (outputPortBytes>sizeof(packetToSend.m_data))?sizeof(packetToSend.m_data):outputPortBytes;
	if( outputPortBytes || flagsChanged || m_forceSendPacket)
	{
		frameSendIndex += lastReadFrameCnt;
		if(outputPortBytes)
		{
			readSize = outputPortBytes;
			result = sceVoiceReadFromOPort(m_voiceOutPort, packetToSend.m_data, &readSize );
			if (result != SCE_OK)
			{
				app.DebugPrintf("sceVoiceReadFromOPort failed %0x\n", result);
				assert(0);
				return;
			}
			lastReadFrameCnt = readSize/portInfo.frameSize;
			assert(readSize%portInfo.frameSize == 0);

			packetToSend.m_numFrames = lastReadFrameCnt;
			packetToSend.m_frameSendIndex = frameSendIndex;
			packetToSend.setChecksum(readSize);
		}
		else
		{
			readSize = 0;
			packetToSend.m_numFrames = 0;
			packetToSend.m_frameSendIndex = frameSendIndex;
			packetToSend.setChecksum(readSize);

		}

					
		int packetSize = packetToSend.getPacketSize(readSize);

		EnterCriticalSection(&m_csRemoteConnections);

		// send this packet out to all our remote connections
		for(int rIdx=0;rIdx<m_remoteConnections.size(); rIdx++)
		{
			SQRVoiceConnection* pVoice = m_remoteConnections[rIdx];
			if(pVoice->m_bConnected)
 				m_remoteConnections[rIdx]->SendInternal(&packetToSend, packetSize);
		}

		LeaveCriticalSection(&m_csRemoteConnections);
	}
	m_forceSendPacket = false; 
}

bool g_bPlaying = false;

void SonyVoiceChat_Orbis::playAllReceivedData()
{
	EnterCriticalSection(&m_csRemoteConnections);
	// write all the incoming data from the network to each of the input voices
	for(int rIdx=0;rIdx<m_remoteConnections.size(); rIdx++)
	{
		SQRVoiceConnection* pVoice = m_remoteConnections[rIdx];
		VoicePacket packet;
		while(pVoice->getNextPacket(packet))	// MGH -  changed to a while loop, so all the packets are sent to the voice port, and it can handle delayed packets due to the size of it's internal buffer
		{
			int frameGap;
			if (pVoice->m_nextExpectedFrameIndex == packet.m_frameSendIndex)
			{
				// no voice frame drop, continuous frames
				frameGap = 0;
				app.DebugPrintf(sc_verbose, "index@%d gets expected frame\n",pVoice->m_nextExpectedFrameIndex);
				pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames;
			}
			else if (pVoice->m_nextExpectedFrameIndex < packet.m_frameSendIndex)
			{
				// has voice frame drop, dropped forwarding frames
				frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex;
				app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n",pVoice->m_nextExpectedFrameIndex, frameGap);
				pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames;
			}
			else if (pVoice->m_nextExpectedFrameIndex > packet.m_frameSendIndex)
			{
				// has voice frame drop, dropped preceding frames, no reset on pVoice->m_nextExpectedFrameIndex
				frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex;
				app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n", pVoice->m_nextExpectedFrameIndex, frameGap);
			}

			SceVoiceBasePortInfo        portInfo;
			int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo );
			if (result != SCE_OK)
			{
				app.DebugPrintf(sc_verbose, "sceVoiceGetPortInfo LoopbackVoiceInPort failed %x\n", result);
				assert(0);
				LeaveCriticalSection(&m_csRemoteConnections);
				return;
			}
			uint32_t writeSize = packet.m_numFrames * portInfo.frameSize;
			int inputPortBytes = portInfo.numByte;
			inputPortBytes = (inputPortBytes>writeSize)?writeSize:inputPortBytes;
			writeSize = inputPortBytes;
			result = sceVoiceWriteToIPort(pVoice->m_voiceInPort, packet.m_data, &writeSize, frameGap);
			if (result != SCE_OK)
			{
				app.DebugPrintf(sc_verbose, "sceVoiceWriteToIPort failed %0x\n", result);
				assert(0);
				LeaveCriticalSection(&m_csRemoteConnections);
				return;
			}
			if (writeSize != inputPortBytes)
			{
				// libvoice internal voice in port buffer fulls
				app.DebugPrintf(sc_verbose, "internal voice in port buffer fulls. \n");
			}
			packet.m_numFrames = 0;

			// copy the flags
			for(int flagIndex=0;flagIndex<MAX_LOCAL_PLAYER_COUNT;flagIndex++)
				pVoice->m_remotePlayerFlags[flagIndex] = packet.m_localPlayerFlags[flagIndex];
		}
	}
	LeaveCriticalSection(&m_csRemoteConnections);

}

void SonyVoiceChat_Orbis::tick()
{
	if(m_bInitialised)
	{
// 		DWORD tick = GetTickCount();
// 		static DWORD lastTick = 0;
// 		app.DebugPrintf("Time since last voice tick : %d ms\n", tick - lastTick);
// 		lastTick = tick;
		g_frameNum++;
#ifdef USE_PCM_MIC_DATA
		sendPCMMicData();
#endif
		sendAllVoiceData();
		playAllReceivedData();
		
		EnterCriticalSection(&m_csRemoteConnections);
		
		for(int i=m_remoteConnections.size()-1;i>=0;i--)
		{
			if(m_remoteConnections[i]->m_bFlaggedForShutdown)
			{
				delete m_remoteConnections[i];
				m_remoteConnections.erase(m_remoteConnections.begin() + i);
			}
		}

		LeaveCriticalSection(&m_csRemoteConnections);

		// MGH -  added to hopefully fix a lockup with shutdowns happening on different threads, when more than 1 player leaves the game
		for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
		{
			if(m_localVoiceDevices[i].m_bFlaggedForShutdown)
			{
				m_localVoiceDevices[i].shutdownWhenFlagged();
			}
		}

	}
}



bool SonyVoiceChat_Orbis::hasMicConnected(SQRNetworkPlayer* pNetPlayer)
{
	if(pNetPlayer->IsLocal())
	{
		return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected;
	}
	else
	{
		EnterCriticalSection(&m_csRemoteConnections);
		for(int i=0;i<m_remoteConnections.size();i++)
		{
			SQRVoiceConnection* pVoice = m_remoteConnections[i];
			if(pVoice->m_remoteRoomMemberId == pNetPlayer->m_roomMemberId)
			{
				bool bMicConnected = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected;
				LeaveCriticalSection(&m_csRemoteConnections);
				return bMicConnected;
			}
		}
		LeaveCriticalSection(&m_csRemoteConnections);
	}
	// if we get here we've not found the player, panic!!
	assert(0);
	return false;
}

void SonyVoiceChat_Orbis::mute( bool bMute )
{
}

void SonyVoiceChat_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */
{
}

void SonyVoiceChat_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */
{
}

bool SonyVoiceChat_Orbis::isMuted()
{
}

bool SonyVoiceChat_Orbis::isMutedPlayer( const PlayerUID& memberUID) 
{
 	return false;
}

bool SonyVoiceChat_Orbis::isMutedLocalPlayer() 
{
 	return false;
}


bool SonyVoiceChat_Orbis::isTalking(SQRNetworkPlayer* pNetPlayer)
{
	if(pNetPlayer->IsLocal())
	{
		return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking;
	}
	else
	{
		EnterCriticalSection(&m_csRemoteConnections);
		for(int i=0;i<m_remoteConnections.size();i++)
		{
			SQRVoiceConnection* pVoice = m_remoteConnections[i];
			if(pVoice->m_remoteRoomMemberId == pNetPlayer->m_roomMemberId)
			{
				bool bTalking = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking;
				LeaveCriticalSection(&m_csRemoteConnections);
				return bTalking;
			}
		}
		LeaveCriticalSection(&m_csRemoteConnections);
	}
	// if we get here we've not found the player, panic!!
	assert(0);
	return false;
}


void SQRLocalVoiceDevice::init(SceUserServiceUserId localUserID, bool bChatRestricted)
{
	SceVoiceInitParam           params;
	SceVoicePortParam           portArgs;

	int returnCode = 0;
	m_bChatRestricted = bChatRestricted;

#ifdef USE_PCM_MIC_DATA
	portArgs.portType                  = SCE_VOICE_PORTTYPE_IN_PCMAUDIO;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 100;
	portArgs.volume                    = 1.0f;
	portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000;
	portArgs.pcmaudio.format.dataType  = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
	portArgs.pcmaudio.bufSize          = 4096;
	CreatePort( &m_microphonePort, &portArgs );
#else
	portArgs.portType                  = SCE_VOICE_PORTTYPE_IN_DEVICE;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 0;
	portArgs.volume                    = 1.0f;
	portArgs.device.userId			   = localUserID;
	portArgs.device.type			   = SCE_AUDIO_IN_TYPE_VOICE;
	portArgs.device.index			   = 0;
	CreatePort( &m_microphonePort, &portArgs );

#endif
	portArgs.portType                  = SCE_VOICE_PORTTYPE_OUT_DEVICE;
	portArgs.bMute                     = false;
	portArgs.threshold                 = 0;
	portArgs.volume                    = 1.0f;
	portArgs.device.userId			   = localUserID;
	portArgs.device.type			   = SCE_AUDIO_OUT_PORT_TYPE_VOICE;
	portArgs.device.index			   = 0;
	CreatePort( &m_headsetPort, &portArgs );

	m_localUserID = localUserID;

}



void SQRLocalVoiceDevice::shutdownWhenFlagged()
{

	assert(isValid());
	m_localUserID = SCE_USER_SERVICE_USER_ID_INVALID;
	DeletePort(m_microphonePort);
	DeletePort(m_headsetPort);
	m_bFlaggedForShutdown = false;
}



SQRVoiceConnection* SonyVoiceChat_Orbis::addRemoteConnection( int RudpCxt, SceNpMatching2RoomMemberId peerMemberId)
{
	EnterCriticalSection(&m_csRemoteConnections);
	SQRVoiceConnection* pConn = new SQRVoiceConnection(RudpCxt, peerMemberId);
	m_remoteConnections.push_back(pConn);
	m_forceSendPacket = true;  // new connection, so we'll force a packet through for the flags
	LeaveCriticalSection(&m_csRemoteConnections);
	
	return pConn;
}

void SonyVoiceChat_Orbis::connectPorts(uint32_t inPort, uint32_t outPort)
{
	int returnCode = sceVoiceConnectIPortToOPort(inPort, outPort);
	if (returnCode != SCE_OK )
	{
		app.DebugPrintf("sceVoiceConnectIPortToOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort);
		assert(0);
	}
}
void SonyVoiceChat_Orbis::disconnectPorts(uint32_t inPort, uint32_t outPort)
{
	int returnCode = sceVoiceDisconnectIPortFromOPort(inPort, outPort);
	if (returnCode != SCE_OK )
	{
		app.DebugPrintf("sceVoiceDisconnectIPortFromOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort);
		assert(0);
	}
}


void SonyVoiceChat_Orbis::makeLocalConnections()
{
	// connect all mics to other devices headsets, for local chat 
	for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
	{
		SQRLocalVoiceDevice* pConnectFrom = &m_localVoiceDevices[i];
		if(pConnectFrom->isValid())
		{
			for(int j=0;j<MAX_LOCAL_PLAYER_COUNT;j++)
			{
				SQRLocalVoiceDevice* pConnectTo = &m_localVoiceDevices[j];
				if( (pConnectFrom!=pConnectTo) && pConnectTo->isValid())
				{
					if(pConnectFrom->m_localConnections[j] == false)
					{
						if(pConnectTo->m_bChatRestricted == false && pConnectFrom->m_bChatRestricted == false)
						{
							connectPorts(pConnectFrom->m_microphonePort, pConnectTo->m_headsetPort);
							pConnectFrom->m_localConnections[j] = true;
						}
					}
				}
			}
		}
	}
}

void SonyVoiceChat_Orbis::breakLocalConnections(int playerIdx)
{
	// break any connections with devices that are no longer valid
	for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
	{
		SQRLocalVoiceDevice* pConnectedFrom = &m_localVoiceDevices[i];
		for(int j=0;j<MAX_LOCAL_PLAYER_COUNT;j++)
		{
			if(pConnectedFrom->m_localConnections[j] == true)
			{
				SQRLocalVoiceDevice* pConnectedTo = &m_localVoiceDevices[j];
				if(i==playerIdx || j==playerIdx)
				{
					if(pConnectedTo->m_bChatRestricted == false && pConnectedFrom->m_bChatRestricted == false)
					{
						disconnectPorts(pConnectedFrom->m_microphonePort, pConnectedTo->m_headsetPort);
						pConnectedFrom->m_localConnections[j] = false;
					}
				}
			}
		}
	}
}


void SonyVoiceChat_Orbis::initLocalPlayer(int playerIndex)
{
	if(m_localVoiceDevices[playerIndex].isValid() == false)
	{
		bool chatRestricted = false;
		ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL);

		// create all device ports required
		m_localVoiceDevices[playerIndex].init(ProfileManager.getUserID(playerIndex), chatRestricted);
		m_numLocalDevicesConnected++;
		if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false)
		{
			connectPorts(m_localVoiceDevices[playerIndex].m_microphonePort, m_voiceOutPort);
		}
		m_forceSendPacket = true;  // new local device, so we'll force a packet through for the flags

	}
	makeLocalConnections();
}

void SonyVoiceChat_Orbis::connectPlayer(SQRVoiceConnection* pConnection, int playerIndex)
{
	if((pConnection->m_headsetConnectionMask & (1 << playerIndex)) == 0)
	{
		initLocalPlayer(playerIndex); // added this as we can get a client->client connection coming in first, and the network player hasn't been created yet (so this hasn't been initialised)
		if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false)
		{
			connectPorts(pConnection->m_voiceInPort, m_localVoiceDevices[playerIndex].m_headsetPort);
		}
		pConnection->m_headsetConnectionMask |= (1 << playerIndex);
		app.DebugPrintf("Connecting player %d to rudp context %d\n", playerIndex, pConnection->m_rudpCtx);
		m_forceSendPacket = true;  // new connection, so we'll force a packet through for the flags
	}
}

SQRVoiceConnection* SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx( int RudpCtx )
{
	for(int i=0;i<m_remoteConnections.size();i++)
	{
		if(m_remoteConnections[i]->m_rudpCtx == RudpCtx)
			return m_remoteConnections[i];
	}
	return NULL;
}

void SonyVoiceChat_Orbis::connectPlayerToAll( int playerIndex )
{
	EnterCriticalSection(&m_csRemoteConnections);

	for(int i=0;i<m_remoteConnections.size();i++)
	{
		SonyVoiceChat_Orbis::connectPlayer(m_remoteConnections[i], playerIndex);
	}

	LeaveCriticalSection(&m_csRemoteConnections);
}

SQRVoiceConnection* SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID( SceNpMatching2RoomMemberId roomMemberID )
{
	for(int i=0;i<m_remoteConnections.size();i++)
	{
		if(m_remoteConnections[i]->m_remoteRoomMemberId == roomMemberID)
		{
			return m_remoteConnections[i];
		}
	}

	return NULL;
}

void SonyVoiceChat_Orbis::disconnectLocalPlayer( int localIdx )
{
	EnterCriticalSection(&m_csRemoteConnections);

	if(m_localVoiceDevices[localIdx].m_bChatRestricted == false)
	{
		disconnectPorts(m_localVoiceDevices[localIdx].m_microphonePort, m_voiceOutPort);
	
		for(int i=0;i<m_remoteConnections.size();i++)
		{
			disconnectPorts(m_remoteConnections[i]->m_voiceInPort, m_localVoiceDevices[localIdx].m_headsetPort);
			m_remoteConnections[i]->m_headsetConnectionMask &= (~(1 << localIdx));
			app.DebugPrintf("disconnecting player %d from rudp context %d\n", localIdx, m_remoteConnections[i]->m_rudpCtx);
		}
	}
	LeaveCriticalSection(&m_csRemoteConnections);
		
	breakLocalConnections(localIdx);
	m_localVoiceDevices[localIdx].flagForShutdown();
	m_numLocalDevicesConnected--;

	if(m_numLocalDevicesConnected == 0) // no more local players, kill all the remote connections
	{
		for(int i=0;i<m_remoteConnections.size();i++)
		{
			delete m_remoteConnections[i];
		}
		m_remoteConnections.clear();
	}
}


void SonyVoiceChat_Orbis::disconnectRemoteConnection( SQRVoiceConnection* pVoice )
{
	EnterCriticalSection(&m_csRemoteConnections);

	int voiceIdx = -1;
	for(int i=0;i<m_remoteConnections.size();i++)
	{
		if(m_remoteConnections[i] == pVoice)
		voiceIdx = i;
	}
	assert(voiceIdx>=0);
	if(voiceIdx>=0)
	{
		m_remoteConnections[voiceIdx]->m_bFlaggedForShutdown = true;
	}

	LeaveCriticalSection(&m_csRemoteConnections);

}

void SonyVoiceChat_Orbis::setConnected( int RudpCtx )
{
	SQRVoiceConnection* pVoice = GetVoiceConnectionFromRudpCtx(RudpCtx);
	if(pVoice)
	{
		pVoice->m_bConnected = true;
		m_forceSendPacket = true;
	}
	else
	{
		assert(false);
	}
}




RingBuffer::RingBuffer( int sizeBytes )
{
	buffer = new char[sizeBytes];
	buf_size = sizeBytes;
	buf_full = buf_free = 0;
}


int RingBuffer::Write( char* data, int len_ )
{
	if (len_ <= 0) return len_;
	unsigned int len = (unsigned int)len_;
	unsigned int data_size = buf_size - (buf_free - buf_full);
	if (len > data_size)
		len = data_size;
	data_size = buf_size - (buf_free % buf_size);
	if (data_size > len)
		data_size = len;
	memcpy(buffer + (buf_free % buf_size), data, data_size);
	if (data_size != len)
		memcpy(buffer, data + data_size, len - data_size);
	buf_free += len;
	return len;
}

int RingBuffer::Read( char* data, int max_bytes_ )
{
	if (max_bytes_ <= 0) return max_bytes_;
	unsigned int max_bytes = (unsigned int)max_bytes_;
	unsigned int result = buf_free - buf_full;
	if (result > max_bytes)
		result = max_bytes;
	unsigned int chunk = buf_size - (buf_full % buf_size);
	if (chunk > result)
		chunk = result;
	memcpy(data, buffer + (buf_full % buf_size), chunk);
	if (chunk != result)
		memcpy(data + chunk, buffer, result - chunk);
	buf_full += result;
	return result;
}
