/*----------------------------------------------------------------------------
  ChucK Concurrent, On-the-fly Audio Programming Language
    Compiler and Virtual Machine

  Copyright (c) 2004 Ge Wang and Perry R. Cook.  All rights reserved.
    http://chuck.stanford.edu/
    http://chuck.cs.princeton.edu/

  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 2 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, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  U.S.A.
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// file: ulib_opsc.cpp
// desc: class library for open sound control
//
// author: Philip L. Davidson (philipd@alumni.princeton.edu)
//         Ge Wang (gewang@cs.princeton.edu)
//         Perry R. Cook (prc@cs.princeton.edu)
// date: Spring 2005
//-----------------------------------------------------------------------------

// lo.h ----> lo_osc_types.h needs to import <cstdint> before running itself,
// but ONLY when compiling this file, and NOT when compiling liblo .c files
// so, set a flag | REFACTOR-2017
#ifdef __PLATFORM_WIN32__
#define ULIB_OPSC_CPP
#endif

#ifdef __UNITY_FLAT__
#include "lo.h"
#else
#include "lo/lo.h"
#endif

#include "ulib_opsc.h"
#include "chuck_type.h"
#include "chuck_vm.h"
#include "chuck_compile.h"
#include "chuck_dl.h"
#include "util_opsc.h"
#include "chuck_instr.h"
#include "util_thread.h"
#include "util_buffers.h"
#include "util_string.h"


#if _MSC_VER
#define snprintf _snprintf
#endif


const std::string CK_OSC_TYPETAG_CHARS = "ifs";

struct OscMsg
{
    std::string path;
    std::string type;

    struct OscArg
    {
        OscArg() : i(0), f(0), s() { }
        OscArg(int _i) : i(_i), f(0), s() { }
        OscArg(float _f) : i(0), f(_f), s() { }
        OscArg(const std::string &_s) : i(0), f(0), s(_s) { }

        int i;
        float f;
        std::string s;
    };

    std::vector<OscArg> args;
};


// TODO: use this to cache ck objects to not leak memory so rampantly
struct CkOscMsg
{
    OscMsg msg;

    Chuck_String *path;
    Chuck_String *type;
    std::vector<Chuck_Object> args;
};


class OscIn;

class OscInServer
{
public:

    static OscInServer *forPort(int port)
    {
        if(s_oscIn.count(port) == 0)
        {
            s_oscIn[port] = new OscInServer(port);
        }

        return s_oscIn[port];
    }

    static void shutdownAll()
    {

    }

    void addMethod(const std::string &method, OscIn * obj)
    {
        OscInMsg msg;

        msg.msg_type = OscInMsg::ADD_METHOD;
        methodToPathAndType(method, msg.path, msg.nopath, msg.type, msg.notype);
        msg.obj = obj;

        m_inMsgBuffer.put(msg);
    }

    void removeMethod(const std::string &method, OscIn * obj)
    {
        OscInMsg msg;

        msg.msg_type = OscInMsg::REMOVE_METHOD;
        methodToPathAndType(method, msg.path, msg.nopath, msg.type, msg.notype);
        msg.obj = obj;

        m_inMsgBuffer.put(msg);
    }

    void removeAllMethods(OscIn * obj)
    {
        OscInMsg msg;

        msg.msg_type = OscInMsg::REMOVEALL_METHODS;
        msg.obj = obj;

        m_inMsgBuffer.put(msg);
    }

    int port()
    {
        return m_assignedPort;
    }

private:

    static std::map<int, OscInServer *> s_oscIn;

#ifdef WIN32
    static unsigned int __stdcall s_server_cb(void *data)
#else
    static void *s_server_cb(void *data)
#endif // WIN32
    {
        OscInServer * _this = (OscInServer *) data;

#ifdef WIN32
        return (t_CKINT)_this->server_cb();
#else
        return _this->server_cb();
#endif
    }

    static void s_err_handler(int num, const char *msg, const char *where)
    {
        if(msg)
            EM_error3("OscIn: error: %s", msg);
        else
            EM_error3("OscIn: unknown error");
    }

    static void methodToPathAndType(const std::string &method,
                                    std::string &path, t_CKBOOL &nopath,
                                    std::string &type, t_CKBOOL &notype)
    {
        t_CKINT comma_pos = method.find(',');

        if(comma_pos != method.npos)
        {
            path = ltrim(rtrim(method.substr(0, comma_pos)));
            nopath = FALSE;

            std::string dirty_type = method.substr(comma_pos+1);
            type.clear();
            for(int i = 0; i < dirty_type.size(); i++)
                if(CK_OSC_TYPETAG_CHARS.find(dirty_type[i]) != std::string::npos)
                    type.push_back(dirty_type[i]);
            notype = FALSE;
        }
        else
        {
            path = ltrim(rtrim(method));
            nopath = (path.size() == 0);
            notype = TRUE;
        }
    }

    struct OscInMsg
    {
        enum Type
        {
            NO_METHOD,
            ADD_METHOD,
            REMOVE_METHOD,
            REMOVEALL_METHODS,
        };

        Type msg_type;
        std::string path;
        t_CKBOOL nopath;
        std::string type;
        t_CKBOOL notype;
        OscIn * obj;

        // 1.4.1.0 (ge) added
        OscInMsg()
        : msg_type(NO_METHOD), nopath(FALSE), notype(FALSE), obj(NULL) { }
    };

    OscInServer(int port) :
    m_port(port),
    m_inMsgBuffer(CircularBuffer<OscInMsg>(1024)),
    m_thread(XThread()),
    m_quit(false),
    m_assignedPort(-1)
    {
        m_thread.start(s_server_cb, this);
    }

    void *server_cb();

    XThread m_thread;
    bool m_quit;
    const int m_port;
    int m_assignedPort;
    CircularBuffer<OscInMsg> m_inMsgBuffer;

    struct Method
    {
        std::string path;
        t_CKBOOL nopath;
        std::string type;
        t_CKBOOL notype;

        bool operator==(const Method &m) const
        {
            return path == m.path && nopath == m.nopath && type == m.type && notype == m.notype;
        }
    };

    std::map<OscIn *, std::list<Method> > m_methods;
};

std::map<int, OscInServer *> OscInServer::s_oscIn;



class OscIn
{
public:
    OscIn(Chuck_Event * event, Chuck_VM * vm) :
    m_event(event),
    m_vm(vm),
    m_port(-1),
    m_oscMsgBuffer(CircularBuffer<OscMsg>(1024))
    {
        m_eventBuffer = m_vm->create_event_buffer();
    }

    ~OscIn()
    {
        removeAllMethods();

        CBufferSimple * t_eventBuffer = m_eventBuffer;
        m_eventBuffer = NULL;
        m_vm->destroy_event_buffer(t_eventBuffer);

        m_vm = NULL;
        m_event = NULL;
    }


    void addMethod(const std::string &method) { OscInServer::forPort(m_port)->addMethod(method, this); }
    void removeMethod(const std::string &method) { OscInServer::forPort(m_port)->removeMethod(method, this); }
    void removeAllMethods() { OscInServer::forPort(m_port)->removeAllMethods(this); }

    t_CKBOOL get(OscMsg &msg)
    {
        return m_oscMsgBuffer.get(msg);
    }

    static int s_handler(const char *path, const char *types,
                         lo_arg **argv, int argc,
                         lo_message msg, void *user_data)
    {
        OscIn * _this = (OscIn *) user_data;
        return _this->handler(path, types, argv, argc, msg);
    }

    int port(int port) { m_port = port; return m_port; }
    int port()
    {
        // 1.5.0.0 (ge) simply return the current m_port
        return m_port;
        // previously (which returns an assigned port is always set)
        // return OscInServer::forPort(m_port)->port();
    }

private:

    Chuck_VM * m_vm;
    Chuck_Event * m_event;
    int m_port;
    CBufferSimple * m_eventBuffer;
    CircularBuffer<OscMsg> m_oscMsgBuffer;

    int handler(const char *path, const char *types,
                lo_arg **argv, int argc, lo_message _msg)
    {
        OscMsg msg;
        msg.path = path;
        msg.type = types;

        for(int i = 0; i < argc; i++)
        {
            OscMsg::OscArg arg;

            switch(types[i])
            {
                case 'i': arg.i = argv[i]->i; break;
                case 'f': arg.f = argv[i]->f; break;
                case 's': arg.s = &(argv[i]->s); break;
                default:
                    EM_error3("OscIn: error: unhandled OSC type '%c'", types[i]);
            }

            msg.args.push_back(arg);
        }

        m_oscMsgBuffer.put(msg);
        m_vm->queue_event(m_event, 1, m_eventBuffer);

        return -1;
    }
};


void *OscInServer::server_cb()
{
    lo_server m_server = NULL;

    if(m_port > 0)
    {
        char portStr[32];
        snprintf(portStr, 32, "%d", m_port);
        m_server = lo_server_new(portStr, s_err_handler);
    }
    else
    {
        m_server = lo_server_new(NULL, s_err_handler);
    }

    if(m_server == NULL)
    {
        EM_error3("OscIn: error: unable to create server instance");
        return NULL;
    }

    m_assignedPort = lo_server_get_port(m_server);

    EM_log(CK_LOG_INFO, "OscIn: starting OSC server at port '%d'", m_port);

    while(!m_quit)
    {
        OscInMsg msg;
        while(m_inMsgBuffer.get(msg))
        {
            switch(msg.msg_type)
            {
                case OscInMsg::NO_METHOD: // 1.4.1.0 (ge) added
                {
                    // just skip
                    continue;
                }
                break;

                case OscInMsg::ADD_METHOD:
                {
                    if(lo_server_add_method(m_server,
                                            msg.nopath ? NULL : msg.path.c_str(),
                                            msg.notype ? NULL : msg.type.c_str(),
                                            OscIn::s_handler, msg.obj))
                    {
                        if(!m_methods.count(msg.obj))
                            m_methods[msg.obj] = std::list<Method>();

                        Method m;
                        m.path = msg.path;
                        m.nopath = msg.nopath;
                        m.type = msg.type;
                        m.notype = msg.notype;
                        m_methods[msg.obj].push_back(m);
                    }
                    else
                    {
                        EM_error3("OscIn: error: add_method failed for %s, %s", msg.path.c_str(), msg.type.c_str());
                    }
                }
                break;

                case OscInMsg::REMOVE_METHOD:
                {
                    lo_server_del_method(m_server,
                                         msg.nopath ? NULL : msg.path.c_str(),
                                         msg.notype ? NULL : msg.type.c_str());
                    if(m_methods.count(msg.obj))
                    {
                        Method m;
                        m.path = msg.path;
                        m.nopath = msg.nopath;
                        m.type = msg.type;
                        m.notype = msg.notype;
                        m_methods[msg.obj].remove(m);
                    }
                }
                break;

                case OscInMsg::REMOVEALL_METHODS:
                {
                    if(m_methods.count(msg.obj))
                    {
                        for(std::list<Method>::iterator i = m_methods[msg.obj].begin();
                            i != m_methods[msg.obj].end(); i++)
                        {
                            lo_server_del_method(m_server,
                                                 i->nopath ? NULL : i->path.c_str(),
                                                 i->notype ? NULL : i->type.c_str());
                        }

                        m_methods[msg.obj].clear();
                        m_methods.erase(msg.obj);
                    }
                }
                break;
            }
        }

        lo_server_recv_noblock(m_server, 2);
    }

    lo_server_free(m_server);

    EM_log(CK_LOG_INFO, "OscIn: shutting down OSC server", m_port);

    return NULL;
}

class OscOut
{
public:
    OscOut()
    {
        m_address = NULL;
        m_message = NULL;
        // 1.4.1.1 (ge) added
        m_port = 0;
    }

    ~OscOut()
    {
        if(m_address != NULL)
        {
            lo_address_free(m_address);
            m_address = NULL;
        }

        if(m_message != NULL)
        {
            lo_message_free(m_message);
            m_message = NULL;
        }
    }

    t_CKBOOL setDestination(const std::string &host, int port)
    {
        if( m_address != NULL )
        {
            lo_address_free(m_address);
            m_address = NULL;
        }

        char portStr[32];
        snprintf( portStr, 32, "%d", port );

        // 1.4.1.1 (ge) remember current hostname and port
        m_hostname = host;
        m_port = port;

        // allocate address
        m_address = lo_address_new( host.c_str(), portStr );

        // check it
        if( m_address == NULL )
        {
            const char * msg = ""; // lo_address_errstr(NULL); // 1.4.1.0 commented out
            EM_error3( "OscOut: error: failed to set destination address '%s:%d'%s%s",
                       host.c_str(), port, msg?": ":"", msg?msg:"");
            return FALSE;
        }

        return TRUE;
    }

    t_CKBOOL start(const std::string &path, const std::string &arg)
    {
        return start(path + "," + arg);
    }

    t_CKBOOL start(const std::string &method)
    {
        std::string path;
        t_CKINT comma_pos = method.find(',');
        if(comma_pos != method.npos) m_path = method.substr(0, comma_pos);
        else m_path = method;

        m_message = lo_message_new();

        return TRUE;
    }

    t_CKBOOL add(t_CKINT i)
    {
        if(m_message == NULL)
        {
            EM_error3("OscOut: error: attempt to add argument to message with no OSC address");
            return FALSE;
        }

        lo_message_add_int32(m_message, (int32_t)i);

        return TRUE;
    }

    t_CKBOOL add(t_CKFLOAT f)
    {
        if(m_message == NULL)
        {
            EM_error3("OscOut: error: attempt to add argument to message with no OSC address");
            return FALSE;
        }

        lo_message_add_float(m_message, (float)f);

        return TRUE;
    }

    t_CKBOOL add(const std::string &s)
    {
        if(m_message == NULL)
        {
            EM_error3("OscOut: error: attempt to add argument to message with no OSC address");
            return FALSE;
        }

        lo_message_add_string(m_message, s.c_str());

        return TRUE;
    }

    t_CKBOOL send()
    {
        if(m_message == NULL || m_address == NULL || m_path.size() == 0)
        {
            EM_error3("OscOut: error: attempt to send message with no destination or OSC address");
            return FALSE;
        }

        int result = lo_send_message(m_address, m_path.c_str(), m_message);

        lo_message_free(m_message);
        m_message = NULL;

        if(result == -1)
        {
            const char *msg = lo_address_errstr(m_address);
            // 1.4.1.1 (ge) updated to also print out hostname:port
            EM_error3("OscOut: error: sending OSC message%s%s (%s:%d)",
                      msg?": ":"", msg?msg:"", m_hostname.c_str(), m_port);
            return FALSE;
        }

        return TRUE;
    }

private:
    lo_address m_address;
    std::string m_path;
    lo_message m_message;
    // 1.4.1.1 (ge) added to print hostname:port in error reporting
    std::string m_hostname;
    int m_port;
};


static t_CKUINT oscin_offset_data = 0;

static t_CKUINT oscout_offset_data = 0;

static t_CKUINT oscmsg_offset_address = 0;
static t_CKUINT oscmsg_offset_typetag = 0;
static t_CKUINT oscmsg_offset_args = 0;

static t_CKUINT oscarg_offset_type = 0;
static t_CKUINT oscarg_offset_i = 0;
static t_CKUINT oscarg_offset_f = 0;
static t_CKUINT oscarg_offset_s = 0;

Chuck_Type * g_OscMsgType = NULL;
Chuck_Type * g_OscArgType = NULL;

//-----------------------------------------------------------------------------
// name: OscOut
// desc:
//-----------------------------------------------------------------------------
#pragma mark - OscOut

CK_DLL_CTOR(oscout_ctor)
{
    OBJ_MEMBER_INT(SELF, oscout_offset_data) = (t_CKINT) new OscOut;
}

CK_DLL_DTOR(oscout_dtor)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);
    SAFE_DELETE(out);
    OBJ_MEMBER_INT(SELF, oscout_offset_data) = 0;
}

CK_DLL_MFUN(oscout_dest)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    Chuck_String *host = GET_NEXT_STRING(ARGS);
    t_CKINT port = GET_NEXT_INT(ARGS);

    if(host == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscOut.start: argument 'host' is null");
        goto error;
    }

    if(!out->setDestination(host->str(), port))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_start)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    Chuck_String *method = GET_NEXT_STRING(ARGS);

    if(method == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscOut.start: argument 'method' is null");
        goto error;
    }

    if(!out->start(method->str()))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_startDest)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    Chuck_String *method = GET_NEXT_STRING(ARGS);
    Chuck_String *host = GET_NEXT_STRING(ARGS);
    t_CKINT port = GET_NEXT_INT(ARGS);

    if(method == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscOut.start: argument 'method' is null");
        goto error;
    }

    if(host == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscOut.start: argument 'host' is null");
        goto error;
    }

    if(!out->setDestination(host->str(), port))
        goto error;

    if(!out->start(method->str()))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_addInt)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    t_CKINT i = GET_NEXT_INT(ARGS);

    if(!out->add(i))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_addFloat)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    t_CKFLOAT f = GET_NEXT_FLOAT(ARGS);

    if(!out->add(f))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_addString)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    Chuck_String *s = GET_NEXT_STRING(ARGS);

    if(!out->add(s->str()))
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}

CK_DLL_MFUN(oscout_send)
{
    OscOut * out = (OscOut *) OBJ_MEMBER_INT(SELF, oscout_offset_data);

    if(!out->send())
        goto error;

    RETURN->v_object = SELF;

    return;

error:
    RETURN->v_object = NULL;
}


//-----------------------------------------------------------------------------
// name: OscArg
// desc:
//-----------------------------------------------------------------------------
#pragma mark - OscArg

CK_DLL_CTOR(oscarg_ctor)
{
    Chuck_String *type = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED);
    SAFE_ADD_REF(type);
    OBJ_MEMBER_STRING(SELF, oscarg_offset_type) = type;

    OBJ_MEMBER_INT(SELF, oscarg_offset_i) = 0;

    OBJ_MEMBER_FLOAT(SELF, oscarg_offset_f) = 0.0;

    Chuck_String *s = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED);
    SAFE_ADD_REF(s);
    OBJ_MEMBER_STRING(SELF, oscarg_offset_s) = s;
}

CK_DLL_DTOR(oscarg_dtor)
{
    SAFE_RELEASE(OBJ_MEMBER_STRING(SELF, oscarg_offset_type));
    OBJ_MEMBER_STRING(SELF, oscarg_offset_type) = NULL;

    SAFE_RELEASE(OBJ_MEMBER_STRING(SELF, oscarg_offset_s));
    OBJ_MEMBER_STRING(SELF, oscarg_offset_s) = NULL;
}



//-----------------------------------------------------------------------------
// name: OscIn
// desc:
//-----------------------------------------------------------------------------
#pragma mark - OscIn

CK_DLL_CTOR(oscin_ctor)
{
    OBJ_MEMBER_INT(SELF, oscin_offset_data) = (t_CKINT) new OscIn((Chuck_Event *) SELF, SHRED->vm_ref);
}

CK_DLL_DTOR(oscin_dtor)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);
    SAFE_DELETE(in);
    OBJ_MEMBER_INT(SELF, oscin_offset_data) = 0;
}

CK_DLL_MFUN(oscin_setport)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    t_CKINT port = GET_NEXT_INT(ARGS);

    RETURN->v_int = in->port(port);
}

CK_DLL_MFUN(oscin_getport)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    RETURN->v_int = in->port();
}

CK_DLL_MFUN(oscin_addAddress)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    Chuck_String *address = GET_NEXT_STRING(ARGS);

    if(address == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscIn.address: argument 'address' is null");
        goto error;
    }

    in->addMethod(address->str());

error:
    return;
}

CK_DLL_MFUN(oscin_removeAddress)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    Chuck_String *address = GET_NEXT_STRING(ARGS);

    if(address == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscIn.address: argument 'address' is null");
        goto error;
    }

    in->removeMethod(address->str());

error:
    return;
}

CK_DLL_MFUN(oscin_removeAllAddresses)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    in->removeAllMethods();

// error:
    return;
}

CK_DLL_MFUN(oscin_listenAll)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    in->addMethod("");

// error:
    return;
}

CK_DLL_MFUN(oscin_msg)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    in->removeAllMethods();

// error:
    return;
}

CK_DLL_MFUN(oscin_recv)
{
    OscIn * in = (OscIn *) OBJ_MEMBER_INT(SELF, oscin_offset_data);

    Chuck_Object * msg_obj = GET_NEXT_OBJECT(ARGS);
    OscMsg msg;

    Chuck_Array4 *args_obj = NULL;
    Chuck_Type * oscarg_type = NULL;

    int i;

    if(msg_obj == NULL)
    {
        throw_exception(SHRED, "NullPtrException", "OscIn.recv: argument 'msg' is null");
        goto error;
    }

    RETURN->v_int = in->get(msg);

    OBJ_MEMBER_STRING(msg_obj, oscmsg_offset_address)->set( msg.path );
    OBJ_MEMBER_STRING(msg_obj, oscmsg_offset_typetag)->set( msg.type );

    args_obj = (Chuck_Array4 *) OBJ_MEMBER_OBJECT(msg_obj, oscmsg_offset_args);
    args_obj->clear();

    oscarg_type = type_engine_find_type( SHRED->vm_ref->env(), str2list("OscArg") );

    for(i = 0; i < msg.args.size(); i++)
    {
        Chuck_Object * arg_obj = instantiate_and_initialize_object(oscarg_type, SHRED);
        // HACK: manually call osc_arg ctor
        oscarg_ctor(arg_obj, NULL, VM, SHRED, API);

        switch(msg.type[i])
        {
            case 'i':
                OBJ_MEMBER_STRING(arg_obj, oscarg_offset_type)->set( "i" );
                OBJ_MEMBER_INT(arg_obj, oscarg_offset_i) = msg.args[i].i;
                break;
            case 'f':
                OBJ_MEMBER_STRING(arg_obj, oscarg_offset_type)->set( "f" );
                OBJ_MEMBER_FLOAT(arg_obj, oscarg_offset_f) = msg.args[i].f;
                break;
            case 's':
                OBJ_MEMBER_STRING(arg_obj, oscarg_offset_type)->set( "s" );
                OBJ_MEMBER_STRING(arg_obj, oscarg_offset_s)->set( msg.args[i].s );
                break;
        }

        args_obj->push_back((t_CKINT) arg_obj);
    }

    return;

error:
    RETURN->v_int = 0;
}


//-----------------------------------------------------------------------------
// name: OscMsg
// desc:
//-----------------------------------------------------------------------------
#pragma mark - OscMsg

CK_DLL_CTOR(oscmsg_ctor)
{
    Chuck_String *address = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED);
    SAFE_ADD_REF(address);
    OBJ_MEMBER_STRING(SELF, oscmsg_offset_address) = address;

    Chuck_String *typetag = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED);
    SAFE_ADD_REF(typetag);
    OBJ_MEMBER_STRING(SELF, oscmsg_offset_typetag) = typetag;

    Chuck_Array4 *args = new Chuck_Array4(TRUE);
    initialize_object(args, SHRED->vm_ref->env()->t_array);
    args->clear();
    SAFE_ADD_REF(args);
    OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args) = args;
}

CK_DLL_DTOR(oscmsg_dtor)
{
    SAFE_RELEASE(OBJ_MEMBER_STRING(SELF, oscmsg_offset_address));
    OBJ_MEMBER_STRING(SELF, oscmsg_offset_address) = NULL;

    SAFE_RELEASE(OBJ_MEMBER_STRING(SELF, oscmsg_offset_typetag));
    OBJ_MEMBER_STRING(SELF, oscmsg_offset_typetag) = NULL;

    SAFE_RELEASE(OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args));
    OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args) = NULL;
}

CK_DLL_MFUN(oscmsg_numArgs)
{
    Chuck_Array4 *args_obj = (Chuck_Array4 *) OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args);
    RETURN->v_int = args_obj->size();
}

CK_DLL_MFUN(oscmsg_getInt)
{
    int i = GET_NEXT_INT(ARGS);
    Chuck_Array4 *args_obj = (Chuck_Array4 *) OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args);
    Chuck_Object *arg_obj;

    // check bounds
    if(i >= 0 && i < args_obj->size())
    {
        args_obj->get(i, (t_CKUINT *) &arg_obj);
        RETURN->v_int = OBJ_MEMBER_INT(arg_obj, oscarg_offset_i);
    }
    else
    {
        RETURN->v_int = 0;
    }
}

CK_DLL_MFUN(oscmsg_getFloat)
{
    int i = GET_NEXT_INT(ARGS);
    Chuck_Array4 *args_obj = (Chuck_Array4 *) OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args);
    Chuck_Object *arg_obj;
    if(i >= 0 && i < args_obj->size())
    {
        args_obj->get(i, (t_CKUINT *) &arg_obj);
        RETURN->v_float = OBJ_MEMBER_FLOAT(arg_obj, oscarg_offset_f);
    }
    else
        RETURN->v_float = 0;
}

CK_DLL_MFUN(oscmsg_getString)
{
    int i = GET_NEXT_INT(ARGS);
    Chuck_Array4 *args_obj = (Chuck_Array4 *) OBJ_MEMBER_OBJECT(SELF, oscmsg_offset_args);
    Chuck_Object *arg_obj;

    if(i >= 0 && i < args_obj->size())
    {
        args_obj->get(i, (t_CKUINT *) &arg_obj);
        RETURN->v_string = OBJ_MEMBER_STRING(arg_obj, oscarg_offset_s);
    }
    else
        RETURN->v_string = NULL;
}

static t_CKUINT osc_send_offset_data = 0;
static t_CKUINT osc_recv_offset_data = 0;
static t_CKUINT osc_address_offset_data = 0;
static Chuck_Type * osc_addr_type_ptr = 0;


DLL_QUERY opensoundcontrol_query ( Chuck_DL_Query * query )
{
    /*** OscOut ***/
    query->begin_class(query, "OscOut", "Object");
    query->doc_class(query, "Class for sending Open Sound Control (OSC) messages. See examples for usage.");
    oscout_offset_data = query->add_mvar(query, "int", "@OscOut_data", FALSE);

    query->add_ctor(query, oscout_ctor);
    query->add_dtor(query, oscout_dtor);

    query->add_mfun(query, oscout_dest, "OscOut", "dest");
    query->add_arg(query, "string", "hostname");
    query->add_arg(query, "int", "port");
    query->doc_func(query, "Set the destination hostname and port for sending OSC message.");

    query->add_mfun(query, oscout_start, "OscOut", "start");
    query->add_arg(query, "string", "address");
    query->doc_func(query, "Start an OSC message with a particular address.");

    query->add_mfun(query, oscout_startDest, "OscOut", "start");
    query->add_arg(query, "string", "address");
    query->add_arg(query, "string", "host");
    query->add_arg(query, "int", "port");
    query->doc_func(query, "Start an OSC message with a particular address, aimed at a destination host and port.");

    query->add_mfun(query, oscout_addInt, "OscOut", "add");
    query->add_arg(query, "int", "i");
    query->doc_func(query, "Add an integer value to an OSC message.");

    query->add_mfun(query, oscout_addFloat, "OscOut", "add");
    query->add_arg(query, "float", "f");
    query->doc_func(query, "Add a floating-point value to an OSC message.");

    query->add_mfun(query, oscout_addString, "OscOut", "add");
    query->add_arg(query, "string", "s");
    query->doc_func(query, "Add an string value to an OSC message.");

    query->add_mfun(query, oscout_send, "OscOut", "send");
    query->doc_func(query, "Send the current OSC message.");

    // add examples | 1.5.0.0 (ge) added
    query->add_ex( query, "osc/s.ck" );
    query->add_ex( query, "osc/r.ck" );
    query->add_ex( query, "osc/osc-dump.ck" );

    query->end_class(query);


    /*** OscArg ***/

    query->begin_class(query, "OscArg", "Object");

    oscarg_offset_type = query->add_mvar(query, "string", "type", FALSE);
    oscarg_offset_i = query->add_mvar(query, "int", "i", FALSE);
    oscarg_offset_f = query->add_mvar(query, "float", "f", FALSE);
    oscarg_offset_s = query->add_mvar(query, "string", "s", FALSE);

    query->add_ctor(query, oscarg_ctor);
    query->add_dtor(query, oscarg_dtor);

    query->end_class(query);


    /*** OscMsg ***/

    query->begin_class(query, "OscMsg", "Object");
    query->doc_class(query, "Helper class for receiving the contents of an OSC message.");

    oscmsg_offset_address = query->add_mvar(query, "string", "address", FALSE);
    query->doc_var(query, "The OSC address string (e.g., \"/foo/param\")." );
    oscmsg_offset_typetag = query->add_mvar(query, "string", "typetag", FALSE);
    query->doc_var(query, "The OSC type tag string (e.g., \"iif\" for int, int, float).");
    oscmsg_offset_args = query->add_mvar(query, "OscArg[]", "args", FALSE);
    query->doc_var(query, "Array of OscArgs contained in this message.");

    query->add_mfun(query, oscmsg_numArgs, "int", "numArgs");
    query->doc_func(query, "Get the number of arguments contained in this OscMsg.");

    query->add_mfun(query, oscmsg_getInt, "int", "getInt");
    query->add_arg(query, "int", "i");
    query->doc_func(query, "Get argument (at index 'i') as an integer.");

    query->add_mfun(query, oscmsg_getFloat, "float", "getFloat");
    query->add_arg(query, "int", "i");
    query->doc_func(query, "Get argument (at index 'i') as a float.");

    query->add_mfun(query, oscmsg_getString, "string", "getString");
    query->add_arg(query, "int", "i");
    query->doc_func(query, "Get argument (at index 'i') as a string.");

    query->add_ctor(query, oscmsg_ctor);
    query->add_dtor(query, oscmsg_dtor);

    // add examples | 1.5.0.0 (ge) added
    query->add_ex( query, "osc/r.ck" );
    query->add_ex( query, "osc/s.ck" );
    query->add_ex( query, "osc/osc-dump.ck" );
    query->add_ex( query, "multi-msg/r-multi-msg.ck" );

    query->end_class(query);


    /*** OscIn ***/

    // begin class
    query->begin_class( query, "OscIn", "Event" );
    query->doc_class( query, "Class for receiving Open Sound Control (OSC) messages. See examples for usage." );

    // add member reference
    oscin_offset_data = query->add_mvar(query, "int", "@OscOut_data", FALSE);

    query->add_ctor(query, oscin_ctor);
    query->add_dtor(query, oscin_dtor);

    // get port
    query->add_mfun(query, oscin_getport, "int", "port");
    query->doc_func(query, "Get which port to listen on.");
    // set port
    query->add_mfun(query, oscin_setport, "int", "port");
    query->add_arg(query, "int", "p");
    query->doc_func(query, "Set which port to listen on.");

    // add address
    query->add_mfun(query, oscin_addAddress, "void", "addAddress");
    query->add_arg(query, "string", "address");
    query->doc_func(query, "Add an OSC address to receive messages from.");
    // remove address
    query->add_mfun(query, oscin_removeAddress, "void", "removeAddress");
    query->add_arg(query, "string", "address");
    query->doc_func(query, "Stop listening on a particular OSC address.");
    // remove all addresses
    query->add_mfun(query, oscin_removeAllAddresses, "void", "removeAllAddresses");
    query->doc_func(query, "Stop listening on all OSC addresses.");

    // listenAll()
    query->add_mfun(query, oscin_listenAll, "void", "listenAll");
    query->doc_func(query, "Set OscIn to receive messages of any OSC address.");
    // recv
    query->add_mfun(query, oscin_recv, "int", "recv");
    query->add_arg(query, "OscMsg", "msg");
    query->doc_func(query, "Receive the next queued incoming OSC message, returning its contents in `msg`.");

    // add examples | 1.5.0.0 (ge) added
    query->add_ex( query, "osc/r.ck" );
    query->add_ex( query, "osc/s.ck" );
    query->add_ex( query, "osc/osc-dump.ck" );
    query->add_ex( query, "multi-msg/r-multi-msg.ck" );

    // end class definition
    query->end_class(query);


    // get the env
    Chuck_Env * env = query->env();
    Chuck_DL_Func * func = NULL;

    // init base class
    if( !type_engine_import_class_begin( env, "OscSend", "Object",
                                         env->global(), osc_send_ctor,
                                         osc_send_dtor ) )
        return FALSE;

    // add member variable  - OSCTransmitter object
    osc_send_offset_data = type_engine_import_mvar( env, "int", "@OscSend_data", FALSE );
    if( osc_send_offset_data == CK_INVALID_OFFSET ) goto error;

    func = make_new_mfun( "int", "setHost", osc_send_setHost );
    func->add_arg( "string", "address" );
    func->add_arg( "int", "port" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "startMsg", osc_send_startMesg );
    func->add_arg( "string", "address" );
    func->add_arg( "string", "args" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "startMsg", osc_send_startMesg_spec );
    func->add_arg( "string", "spec" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "addInt", osc_send_addInt );
    func->add_arg( "int", "value" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "float", "addFloat", osc_send_addFloat );
    func->add_arg( "float", "value" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "string", "addString", osc_send_addString );
    func->add_arg( "string", "value" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "openBundle", osc_send_openBundle );
    func->add_arg( "time", "value" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "closeBundle", osc_send_closeBundle );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "hold", osc_send_holdMesg );
    func->add_arg( "int", "on" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "kick", osc_send_kickMesg );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    type_engine_import_class_end( env );

    // init base class
    if( !type_engine_import_class_begin( env, "OscEvent", "Event",
                                         env->global(), osc_address_ctor,
                                         osc_address_dtor ) )
        return FALSE;

    // add member variable  - OSCAddress object

    osc_address_offset_data = type_engine_import_mvar( env, "int", "@OscEvent_Data", FALSE );
    if( osc_recv_offset_data == CK_INVALID_OFFSET ) goto error;

    // keep type around for initialization ( so other classes can return it )

    osc_addr_type_ptr = env->class_def;

    func = make_new_mfun( "int", "set", osc_address_set );
    func->add_arg( "string" , "addr" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "hasMsg", osc_address_has_mesg );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "nextMsg", osc_address_next_mesg );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "getInt", osc_address_next_int );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "float", "getFloat", osc_address_next_float );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "string", "getString", osc_address_next_string );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "can_wait", osc_address_can_wait );
    func->doc = "(internal) used by virtual machine for synthronization.";
    if( !type_engine_import_mfun( env, func ) ) goto error;

    type_engine_import_class_end( env );

    // init base class
    if( !type_engine_import_class_begin( env, "OscRecv", "Object",
                                         env->global(), osc_recv_ctor,
                                         osc_recv_dtor ) )
        return FALSE;

    // add member variable  - OSCReceiver object
    osc_recv_offset_data = type_engine_import_mvar( env, "int", "@OscRecv_data", FALSE );
    if( osc_recv_offset_data == CK_INVALID_OFFSET ) goto error;

    func = make_new_mfun( "int", "port", osc_recv_port );
    func->add_arg( "int" , "port" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "listen", osc_recv_listen_port );
    func->add_arg( "int" , "port" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "listen", osc_recv_listen );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "stop", osc_recv_listen_stop );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "add_address", osc_recv_add_address );
    func->add_arg( "OscEvent" , "addr" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "int", "remove_address", osc_recv_remove_address );
    func->add_arg( "OscEvent" , "addr" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "OscEvent", "event", osc_recv_new_address );
    func->add_arg( "string" , "spec" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "OscEvent", "event", osc_recv_new_address_type );
    func->add_arg( "string" , "address" );
    func->add_arg( "string" , "type" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "OscEvent", "address", osc_recv_new_address );
    func->add_arg( "string" , "spec" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    func = make_new_mfun( "OscEvent", "address", osc_recv_new_address_type );
    func->add_arg( "string" , "address" );
    func->add_arg( "string" , "type" );
    if( !type_engine_import_mfun( env, func ) ) goto error;

    type_engine_import_class_end( env );
    return TRUE;

error:

    CK_FPRINTF_STDERR( "class import error!\n" );
    // end the class import
    type_engine_import_class_end( env );
    return FALSE;
}

//----------------------------------------------
// name : osc_send_ctor
// desc : CTOR function
//-----------------------------------------------
CK_DLL_CTOR( osc_send_ctor ) {
    OSC_Transmitter* xmit = new OSC_Transmitter();
    OBJ_MEMBER_INT( SELF, osc_send_offset_data ) = (t_CKINT)xmit;
}

//----------------------------------------------
// name :  osc_send_dtor
// desc : DTOR function
//-----------------------------------------------
CK_DLL_DTOR( osc_send_dtor ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    SAFE_DELETE(xmit);
    OBJ_MEMBER_UINT(SELF, osc_send_offset_data) = 0;
}

//----------------------------------------------
// name :  osc_send_setHost
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_setHost ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    Chuck_String* host = GET_NEXT_STRING(ARGS);
    t_CKINT port = GET_NEXT_INT(ARGS);
    xmit->setHost( (char*) host->str().c_str(), port );
}

//----------------------------------------------
// name :  osc_send_startMesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_startMesg ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    Chuck_String* address = GET_NEXT_STRING(ARGS);
    Chuck_String* args = GET_NEXT_STRING(ARGS);
    xmit->startMessage( (char*) address->str().c_str(), (char*) args->str().c_str() );
}

//----------------------------------------------
// name :  osc_send_startMesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_startMesg_spec ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    Chuck_String* spec = GET_NEXT_STRING(ARGS);
    xmit->startMessage( (char*) spec->str().c_str() );
}

//----------------------------------------------
// name :  osc_send_addInt
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_addInt ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->addInt( GET_NEXT_INT(ARGS) );
}

//----------------------------------------------
// name :  osc_send_addFloat
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_addFloat ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->addFloat( (float)(GET_NEXT_FLOAT(ARGS)) );
}

//----------------------------------------------
// name :  osc_send_addString
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_addString ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->addString( (char*)(GET_NEXT_STRING(ARGS))->str().c_str() );
}

//----------------------------------------------
// name :  osc_send_openBundle
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_openBundle ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    // we should add in an option to translate from chuck-time to current time to timetag.
    // but for now let's just spec. immediately.
    xmit->openBundle(OSCTT_Immediately());
}

//----------------------------------------------
// name :  osc_send_closeBundle
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_closeBundle ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->closeBundle();
}

//----------------------------------------------
// name :  osc_send_holdMesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_holdMesg ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->holdMessage( GET_NEXT_INT(ARGS) != 0 );
}

//----------------------------------------------
// name :  osc_send_kickMesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_send_kickMesg ) {
    OSC_Transmitter* xmit = (OSC_Transmitter *)OBJ_MEMBER_INT(SELF, osc_send_offset_data);
    xmit->kickMessage();
}

//----------------------------------------------
// name :  osc_address_ctor
// desc : CTOR function
//-----------------------------------------------
CK_DLL_CTOR( osc_address_ctor ) {
    OSC_Address_Space * addr = new OSC_Address_Space();
    addr->SELF = SELF;
//    CK_FPRINTF_STDERR("address:ptr %x\n", (uint)addr );
//    CK_FPRINTF_STDERR("self:ptr %x\n", (uint)SELF );
    OBJ_MEMBER_INT(SELF, osc_address_offset_data) = (t_CKINT)addr;
}

CK_DLL_DTOR( osc_address_dtor ) {
    delete (OSC_Address_Space *)OBJ_MEMBER_UINT(SELF, osc_address_offset_data);
    OBJ_MEMBER_UINT(SELF, osc_address_offset_data) = 0;
}

CK_DLL_MFUN( osc_address_set ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    addr->setSpec ( (char*)(GET_NEXT_STRING(ARGS))->str().c_str() );
    RETURN->v_int = 0;
}

//----------------------------------------------
// name : osc_address_can_wait
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_can_wait  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    RETURN->v_int = ( addr->has_mesg() ) ? 0 : 1;
}

//----------------------------------------------
// name : osc_address_has_mesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_has_mesg  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    RETURN->v_int = ( addr->has_mesg() ) ? 1 : 0 ;
}

//----------------------------------------------
// name : osc_address_next_mesg
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_next_mesg  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    RETURN->v_int = ( addr->next_mesg() ) ? 1 : 0 ;
}

//----------------------------------------------
// name : osc_address_next_int
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_next_int  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    RETURN->v_int = addr->next_int();
}

//----------------------------------------------
// name : osc_address_next_float
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_next_float  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    RETURN->v_float = addr->next_float();
}

//----------------------------------------------
// name : osc_address_next_string
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_address_next_string  ) {
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( SELF, osc_address_offset_data );
    char * cs = addr->next_string();
    Chuck_String * ckstr = ( cs ) ? new Chuck_String( cs ) : new Chuck_String("");
    initialize_object( ckstr, SHRED->vm_ref->env()->t_string );
    RETURN->v_string = ckstr;
}




// OscRecv functions
//----------------------------------------------
// name : osc_recv_ctor
// desc : CTOR function
//-----------------------------------------------
CK_DLL_CTOR( osc_recv_ctor )
{
    OSC_Receiver * recv = new OSC_Receiver( SHRED->vm_ref );
    OBJ_MEMBER_INT( SELF, osc_send_offset_data ) = (t_CKINT)recv;
}

//----------------------------------------------
// name : osc_recv_dtor
// desc : DTOR function
//-----------------------------------------------
CK_DLL_DTOR( osc_recv_dtor )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    SAFE_DELETE(recv);
    OBJ_MEMBER_INT(SELF, osc_recv_offset_data) = 0;
}

//----------------------------------------------
// name : osc_recv_port
// desc : specify port to listen on
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_port )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    recv->bind_to_port( (int)GET_NEXT_INT(ARGS) );
}

// need to add a listen function in Receiver which opens up a recv loop on another thread.
// address then subscribe to a receiver to take in events.

//----------------------------------------------
// name : osc_recv_listen
// desc : start listening
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_listen )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    recv->listen();
}

//----------------------------------------------
// name : osc_recv_listen_port
// desc : listen to a given port ( disconnects from current )
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_listen_port )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    recv->listen((int)GET_NEXT_INT(ARGS));
}

//----------------------------------------------
// name : osc_recv_listen_port
// desc : listen to a given port ( disconnects from current )
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_listen_stop )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    recv->stopListening();
}

//----------------------------------------------
// name : osc_recv_add_listener
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_add_address )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    Chuck_Object* addr_obj = GET_NEXT_OBJECT(ARGS); //address object class...
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( addr_obj, osc_address_offset_data );
    recv->add_address( addr );
}

//----------------------------------------------
// name : osc_recv_remove_listener
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_remove_address )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    Chuck_Object* addr_obj = GET_NEXT_OBJECT(ARGS); //listener object class...
    OSC_Address_Space * addr = (OSC_Address_Space *)OBJ_MEMBER_INT( addr_obj, osc_address_offset_data );
    recv->remove_address( addr );
}

//----------------------------------------------
// name : osc_recv_new_address
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_new_address )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    Chuck_String * spec_obj = (Chuck_String*)GET_NEXT_STRING(ARGS); // listener object class...

    // added 1.3.1.1: fix potential race condition
    //OSC_Address_Space * new_addr_obj = recv->new_event( (char*)spec_obj->str.c_str() );
    OSC_Address_Space * new_addr_obj = new OSC_Address_Space( (char*)spec_obj->str().c_str() );

    /* wolf in sheep's clothing
    initialize_object( new_addr_obj , osc_addr_type_ptr ); //initialize in vm
    new_addr_obj->SELF = new_addr_obj;
    OBJ_MEMBER_INT( new_addr_obj, osc_address_offset_data ) = (t_CKINT)new_addr_obj;
    */

    // more correct...?
    Chuck_Event* new_event_obj = new Chuck_Event();
    initialize_object( new_event_obj, osc_addr_type_ptr );
    new_addr_obj->SELF = new_event_obj;
    OBJ_MEMBER_INT( new_event_obj, osc_address_offset_data ) = (t_CKINT)new_addr_obj;

    recv->add_address(new_addr_obj);

    RETURN->v_object = new_event_obj;
}



//----------------------------------------------
// name : osc_recv_new_address
// desc : MFUN function
//-----------------------------------------------
CK_DLL_MFUN( osc_recv_new_address_type )
{
    OSC_Receiver * recv = (OSC_Receiver *)OBJ_MEMBER_INT(SELF, osc_recv_offset_data);
    Chuck_String * addr_obj = (Chuck_String*)GET_NEXT_STRING(ARGS); //listener object class...
    Chuck_String * type_obj = (Chuck_String*)GET_NEXT_STRING(ARGS); //listener object class...
    OSC_Address_Space * new_addr_obj = recv->new_event( (char*)addr_obj->str().c_str(), (char*)type_obj->str().c_str() );

    /* wolf in sheep's clothing
    initialize_object( new_addr_obj , osc_addr_type_ptr ); //initialize in vm
    new_addr_obj->SELF = new_addr_obj;
    OBJ_MEMBER_INT( new_addr_obj, osc_address_offset_data ) = (t_CKINT)new_addr_obj;
    */

    // more correct...?
    Chuck_Event* new_event_obj = new Chuck_Event();
    initialize_object( new_event_obj, osc_addr_type_ptr );
    new_addr_obj->SELF = new_event_obj;
    OBJ_MEMBER_INT( new_event_obj, osc_address_offset_data ) = (t_CKINT)new_addr_obj;

    RETURN->v_object = new_event_obj;
}

// No longer compiling ulib_opsc.cpp | REFACTOR-2017
#ifdef __PLATFORM_WIN32__
#undef ULIB_OPSC_CPP
#endif
