/*
 *  ofOscReceiver.cpp
 *  openFrameworks OSC addon
 *
 *  damian@frey.co.nz
 *
 */

#include "ofOscReceiver.h"
#include <pthread.h>
#include <iostream>
#include <assert.h>

ofOscReceiver::ofOscReceiver()
{
#ifdef TARGET_OSX
	pthread_mutex_init( &mutex, NULL );
#endif
#ifdef TARGET_WIN32
	mutex = CreateMutex( NULL, FALSE, NULL );
#endif
}

void ofOscReceiver::setup( int listen_port )
{
	listen_socket = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS, listen_port ), this );
#ifdef TARGET_OSX
	pthread_create( &thread, NULL, &ofOscReceiver::startThread, (void*)(listen_socket) );
#endif
#ifdef TARGET_WIN32
	thread	= CreateThread( 
							   NULL,              // default security attributes
							   0,                 // use default stack size  
							&ofOscReceiver::startThread,        // thread function 
							   (void*)(listen_socket),             // argument to thread function 
							   0,                 // use default creation flags 
							   NULL);             // we don't the the thread id
	
	
#endif
}

ofOscReceiver::~ofOscReceiver()
{
//	delete listen_socket;
}


/*
#if defined TARGET_OSX
#error target-osx
#elif defined TARGET_WIN32
#error target-win32
#else
#error no target
#endif
 */

#ifdef TARGET_OSX
void*
#endif
#ifdef TARGET_WIN32
DWORD WINAPI
#endif
ofOscReceiver::startThread( void* _socket )
{
	UdpListeningReceiveSocket* socket = (UdpListeningReceiveSocket*)_socket;
	socket->Run();
	return NULL;
}

void ofOscReceiver::ProcessMessage( const osc::ReceivedMessage &m, const IpEndpointName& remoteEndpoint )
{	
	// convert the message to an ofOscMessage
	ofOscMessage* ofMessage = new ofOscMessage();
	
	// set the address
	ofMessage->setAddress( m.AddressPattern() );
	
	// transfer the arguments
	for ( osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
		  arg != m.ArgumentsEnd();
		  ++arg )
	{
		if ( arg->IsInt32() )
			ofMessage->addIntArg( arg->AsInt32Unchecked() );
		else if ( arg->IsFloat() )
			ofMessage->addFloatArg( arg->AsFloatUnchecked() );
		else if ( arg->IsString() )
			ofMessage->addStringArg( arg->AsStringUnchecked() );
		else
		{
			assert( false && "message argument is not int, float, or string" );
		}
	}
	
	// now add to the queue
	
	// at this point we are running inside the thread created by startThread,
	// so anyone who calls hasWaitingMessages() or getNextMessage() is coming 
	// from a different thread
	
	// so we have to practise shared memory management
	
	// grab a lock on the queue
	grabMutex();
	
	// add incoming message on to the queue
	messages.push_back( ofMessage );
	
	// release the lock
	releaseMutex();
}

bool ofOscReceiver::hasWaitingMessages()
{
	// grab a lock on the queue
	grabMutex();

	// check the length of the queue
	int queue_length = (int)messages.size();
	
	// release the lock
	releaseMutex();
	
	// return whether we have any messages
	return queue_length > 0;
}

bool ofOscReceiver::getNextMessage( ofOscMessage* message )
{
	// grab a lock on the queue
	grabMutex();

	// check if there are any to be got
	if ( messages.size() == 0 )
	{
		// no: release the mutex
		releaseMutex();
		return false;
	}
	
	// copy the message from the queue to message
	ofOscMessage* src_message = messages.front();
	message->copy( *src_message );

	// now delete the src message
	delete src_message;
	// and remove it from the queue
	messages.pop_front();

	// release the lock on the queue
	releaseMutex();
	
	// return success
	return true;
}

void ofOscReceiver::grabMutex()
{
#ifdef TARGET_OSX
	pthread_mutex_lock( &mutex );
#endif
#ifdef TARGET_WIN32
	WaitForSingleObject( mutex, INFINITE );
#endif
}

void ofOscReceiver::releaseMutex()
{
#ifdef TARGET_OSX
	pthread_mutex_unlock( &mutex );
#endif
#ifdef TARGET_WIN32
	ReleaseMutex( mutex );
#endif
}