#include "SharedMemoryCommandProcessor.h"

#include "PosixSharedMemory.h"
#include "Win32SharedMemory.h"
#include "Bullet3Common/b3Logging.h"
#include "Bullet3Common/b3Scalar.h"

#include "SharedMemoryBlock.h"


struct SharedMemoryCommandProcessorInternalData
{
	int m_sharedMemoryKey;
	bool m_isConnected;
	SharedMemoryInterface* m_sharedMemory;
	bool	m_ownsSharedMemory;
	bool m_verboseOutput;
	bool m_waitingForServer;
	SharedMemoryStatus m_lastServerStatus;
	SharedMemoryBlock* m_testBlock1;


	SharedMemoryCommandProcessorInternalData()
		:m_sharedMemoryKey(SHARED_MEMORY_KEY),
		m_isConnected(false),
		m_sharedMemory(0),
		m_ownsSharedMemory(false),
		m_verboseOutput(false),
		m_waitingForServer(false),
		m_testBlock1(0)
	{

	}
};

SharedMemoryCommandProcessor::SharedMemoryCommandProcessor()
{
	m_data = new SharedMemoryCommandProcessorInternalData;
	m_data->m_sharedMemoryKey = SHARED_MEMORY_KEY;
#ifdef _WIN32
	m_data->m_sharedMemory = new Win32SharedMemoryClient();
#else
	m_data->m_sharedMemory = new PosixSharedMemory();
#endif
	m_data->m_ownsSharedMemory = true;


}

SharedMemoryCommandProcessor::~SharedMemoryCommandProcessor()
{
	if (m_data->m_isConnected) 
	{
		disconnect();
	}
	if (m_data->m_ownsSharedMemory)
	{
		delete m_data->m_sharedMemory;
	}
	delete m_data;

}

bool SharedMemoryCommandProcessor::connect()
{
	
	if (m_data->m_isConnected)
		return true;

	bool allowCreation = false;
	m_data->m_testBlock1 = (SharedMemoryBlock*)m_data->m_sharedMemory->allocateSharedMemory(
		m_data->m_sharedMemoryKey, SHARED_MEMORY_SIZE, allowCreation);

	if (m_data->m_testBlock1) {
		if (m_data->m_testBlock1->m_magicId != SHARED_MEMORY_MAGIC_NUMBER) {
			b3Error("Error: please start server before client\n");
			m_data->m_sharedMemory->releaseSharedMemory(m_data->m_sharedMemoryKey,
				SHARED_MEMORY_SIZE);
			m_data->m_testBlock1 = 0;
			return false;
		}
		else {
			if (m_data->m_verboseOutput) {
				b3Printf("Connected to existing shared memory, status OK.\n");
			}
			m_data->m_isConnected = true;
		}
	}
	else {
		b3Error("Cannot connect to shared memory");
		return false;
	}
	return true;

}

void SharedMemoryCommandProcessor::disconnect()
{
	if (m_data->m_isConnected && m_data->m_sharedMemory) 
	{
		m_data->m_sharedMemory->releaseSharedMemory(m_data->m_sharedMemoryKey, SHARED_MEMORY_SIZE);
	}
	m_data->m_isConnected = false;

}

bool SharedMemoryCommandProcessor::isConnected() const
{
	return m_data->m_isConnected;
}


bool SharedMemoryCommandProcessor::processCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes)
{
	if (!m_data->m_waitingForServer) {
		if (&m_data->m_testBlock1->m_clientCommands[0] != &clientCmd) {
			m_data->m_testBlock1->m_clientCommands[0] = clientCmd;
		}
		m_data->m_testBlock1->m_numClientCommands++;
		m_data->m_waitingForServer = true;
	}

	return false;
}

bool SharedMemoryCommandProcessor::receiveStatus(struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes)
{

	m_data->m_lastServerStatus.m_dataStream = 0;
	m_data->m_lastServerStatus.m_numDataStreamBytes = 0;

	if (!m_data->m_testBlock1) 
	{
		//m_data->m_lastServerStatus.m_type = CMD_SHARED_MEMORY_NOT_INITIALIZED;
		//return &m_data->m_lastServerStatus;
		//serverStatusOut = m_data->m_lastServerStatus;
		return false;
	}

	if (!m_data->m_waitingForServer) {
		return false;
	}

	if (m_data->m_testBlock1->m_magicId != SHARED_MEMORY_MAGIC_NUMBER)
	{
		//m_data->m_lastServerStatus.m_type = CMD_SHARED_MEMORY_NOT_INITIALIZED;
		//return &m_data->m_lastServerStatus;
		return false;
	}

	if (m_data->m_testBlock1->m_numServerCommands >
		m_data->m_testBlock1->m_numProcessedServerCommands) 
	{
		b3Assert(m_data->m_testBlock1->m_numServerCommands ==
			m_data->m_testBlock1->m_numProcessedServerCommands + 1);

		const SharedMemoryStatus& serverCmd = m_data->m_testBlock1->m_serverCommands[0];
		m_data->m_lastServerStatus = serverCmd;
		m_data->m_lastServerStatus.m_dataStream = m_data->m_testBlock1->m_bulletStreamDataServerToClientRefactor;

		for (int i = 0; i < m_data->m_lastServerStatus.m_numDataStreamBytes; i++)
		{
			bufferServerToClient[i] = m_data->m_testBlock1->m_bulletStreamDataServerToClientRefactor[i];
		}

		m_data->m_testBlock1->m_numProcessedServerCommands++;
		// we don't have more than 1 command outstanding (in total, either server or client)
		b3Assert(m_data->m_testBlock1->m_numProcessedServerCommands ==
			m_data->m_testBlock1->m_numServerCommands);

		if (m_data->m_testBlock1->m_numServerCommands ==
			m_data->m_testBlock1->m_numProcessedServerCommands) {
			m_data->m_waitingForServer = false;
		}
		else {
			m_data->m_waitingForServer = true;
		}

		serverStatusOut = m_data->m_lastServerStatus;

		return true;
	}
	return false;
}

void SharedMemoryCommandProcessor::renderScene()
{
}

void   SharedMemoryCommandProcessor::physicsDebugDraw(int debugDrawFlags)
{
}

void SharedMemoryCommandProcessor::setGuiHelper(struct GUIHelperInterface* guiHelper)
{
}

void SharedMemoryCommandProcessor::setSharedMemoryInterface(class SharedMemoryInterface* sharedMem)
{
	if (m_data->m_sharedMemory && m_data->m_ownsSharedMemory)
	{
		delete m_data->m_sharedMemory;
	}
	m_data->m_ownsSharedMemory = false;
	m_data->m_sharedMemory = sharedMem;

}


void SharedMemoryCommandProcessor::setSharedMemoryKey(int key) 
{ 
	m_data->m_sharedMemoryKey = key; 
}


