/////////////////////////////////////////////////////////////////////////////
// Name:       
// Purpose:  
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by: 
// Created:     
// Copyright:   (C) 2010 Cesar Mauri Loba - CREA Software Systems
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#include "oscinout.h"
#include "spcore/coreruntime.h"
#include <stdexcept>

using namespace spcore;

#define ADDRESS "127.0.0.1"

COscOut::COscOut (int port)
: osc::OutboundPacketStream( m_oscBuffer, COscOut::OSC_OUTPUT_BUFFER_SIZE )
, m_port(port)
, m_pTransmitSocket(NULL)
{
	assert (wxThread::IsMain());
	assert (port> 0 && port< 65535);
}

COscOut::~COscOut ()
{
	Close();
}

void COscOut::Open()
{
	// If already open ignore request
	if (m_pTransmitSocket) return;

	m_pTransmitSocket= new UdpTransmitSocket ( IpEndpointName( ADDRESS, m_port ) );
}

void COscOut::Close()
{
	delete m_pTransmitSocket;
	m_pTransmitSocket= NULL;
}

void COscOut::Send ()
{
	assert (m_pTransmitSocket);

	if (m_pTransmitSocket== NULL)
		throw std::runtime_error ("COscOut: connection closed");
	
	m_pTransmitSocket->Send( Data(), Size());

	Clear();
}

void COscOut::SendSimpleMessage (const char *name, float v)
{
	*this << osc::BeginBundleImmediate << osc::BeginMessage (name) 
		  << v << osc::EndMessage << osc::EndBundle;
	Send();
}

/////////////////////////////

COscIn::COscReceiverThread::COscReceiverThread (UdpListeningReceiveSocket* socket) 
: wxThread (wxTHREAD_JOINABLE)
, m_socket(socket)
{
	assert (m_socket);
}

COscIn::COscReceiverThread::~COscReceiverThread()
{
	delete m_socket;
}

wxThread::ExitCode COscIn::COscReceiverThread::Entry()
{
	wxThread::ExitCode ecode= (wxThread::ExitCode) -1;

	try	{
		m_socket->Run();
		ecode= (wxThread::ExitCode) 0;
	}
	catch(std::exception& e) {
		getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_ERROR, e.what(), "while reading from osc socket");
	}	
	catch(...) {
		getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_ERROR, "Unexpected exception", "while reading from osc socket");
	}
	return ecode;
}

////////////////////////

COscIn::COscIn (int port, PacketListener *listener)
: m_port(port)
, m_listener(listener)
, m_thread (NULL)
{
	assert (wxThread::IsMain());
	assert (port> 0 && port< 65535);
	assert (listener);
}

COscIn::~COscIn()
{
	Close();
}

void COscIn::Open()
{
	// Usually we request a new thread from the main thread. Assert, just in case...
	assert (wxThread::IsMain());

	if (m_thread) return;	// Already opened, ignore.
	
	UdpListeningReceiveSocket* socket= 
		new UdpListeningReceiveSocket(IpEndpointName( IpEndpointName::ANY_ADDRESS, m_port ), m_listener );
		
	assert (m_thread== NULL);
	
   
	m_thread= new COscIn::COscReceiverThread(socket);
	m_thread->Create();
	m_thread->Run();
}

void COscIn::Close()
{
	// Usually we request a new thread from the main thread. Assert, just in case...
	assert (wxThread::IsMain());

	if (m_thread== NULL) return;	// Already closed, ignore.

	// Stop listening 
	m_thread->Break();

	// Wait thread termination & cleanup
	m_thread->Wait();
	delete m_thread;
	m_thread= NULL; 
}
