// MyScript.cpp : Implementation of CMyScript
#include <winsock.h>
#include <stdio.h>
#include <time.h>
#include <process.h>

#include "stdafx.h"
#include "NativeScriptDemo.h"
#include "MyScript.h"

/////////////////////////////////////////////////////////////////////////////
// CMyScript

CMyScript::CMyScript() : 
      fieldsAreOk(false)
{
    // int out buffer
    outValueMax=64;
    outValueCnt = 0;
    outValue = NULL;
    outValue = (BSTR *) calloc(outValueMax,sizeof(BSTR));

    // int out buffer
    errValueMax=64;
    errValueCnt = 0;
    errValue = NULL;
    errValue = (BSTR *) calloc(errValueMax,sizeof(BSTR));

    // int out buffer
    traValueMax=64;
    traValueCnt = 0;
    traValue = NULL;
    traValue = (BSTR *) calloc(traValueMax,sizeof(BSTR));

    // network variables
    //
    UdpSocket = -1;
    ServerUrlList = NULL;
    ServerUrlToUse = 0;
    UdpServerIp = 0;

    // init the the random generator
    srand( rand() ^ time( NULL ));
    srand( rand() ^ getpid( ));

    UdpPacketId = ( rand() << 15 ) ^ rand();
    UdpConnectionId = NULL;

    UdpServerAddress = NULL;
    UdpServerAddressLength = 0;
    UdpSendBuffer = NULL;
    UdpSendBufferLength = 0;
    UdpSendBufferSize = 0;

    senthead = senttail = recvhead = recvtail = NULL;
}

CMyScript::~CMyScript() 
{
    // free the strings , alloctated by getValue
    FreeBSTRs(&outValue,&outValueCnt);
    FreeBSTRs(&errValue,&errValueCnt);
    FreeBSTRs(&traValue,&traValueCnt);

    FREE( ServerUrlList );

    if( UdpSocket >= 0 )
    {
        closesocket( UdpSocket );
        UdpSocket = -1;
    }

    FREE( UdpServerAddress );
    UdpServerAddressLength = 0;

    FREE( UdpSendBuffer );
    UdpSendBufferLength = 0;
    UdpSendBufferSize = 0;

    UdpPacketId = 0;
    FREE( UdpConnectionId );

    while( senthead )
    {
        PacketDelete( 1, senthead );
    }
    while( recvhead )
    {
        PacketDelete( 0, recvhead );
    }
}

// Script methods 


// script instance is created
// we get the pointer to the owning Script node
// inorder to get access to fields (and browser object)

STDMETHODIMP CMyScript::setContainer(Node * container)
{
    script = container;
    if (script) {

        WSADATA WSAData;
        if( WSAStartup( MAKEWORD(1,1), &WSAData ) != 0 )
        {
            AppendErr( _T( "0001" ));
            AppendErr( _T( "WSAStartup failed" ));
        }
    
        // get any field pointers  needed 
        Field *tmp=NULL;

        // modify here : 
        script->getField( L"serverUrl",&tmp);
        serverUrl = tmp; RELEASE(tmp);
    
        if( serverUrl )
        {
            HRESULT hr = S_OK;

            // get the number of strings
            //
            int cnt=0; // 
            hr = serverUrl->getSize( &cnt );

            // malloc the server url list
            //
            FREE( ServerUrlList );
            ServerUrlList = (BSTR*) calloc( cnt, sizeof( BSTR ));

            // query value 
            //
            hr = serverUrl->getValue( cnt, ServerUrlList );

            // test low level output 
            USES_CONVERSION ;
            
            AppendTra( _T( "serverurl" ));
            for( int i = 0; i < cnt; i++ ) 
            {
                // convert to local string type (TCHAR)
                //
                LPCTSTR t;
                t = W2T( ServerUrlList[ i ] );
            
                // put in out queue
                //
                AppendTra( t );
            }
        }    

        script->getField( L"echo",&tmp);
        echo = tmp; RELEASE(tmp); 

        script->getField( L"receive",&tmp);
        receive = tmp; RELEASE(tmp); 

        script->getField( L"errors",&tmp);
        errors = tmp; RELEASE(tmp); 

        script->getField( L"traces",&tmp);
        traces = tmp; RELEASE(tmp); 

        fieldsAreOk= (serverUrl) && (echo) && (receive) && (errors) && (traces);
        if( fieldsAreOk )
        {
            ConnectionInit();
        }

    } else {
        // important zero pointers to clean up cyclic references
        serverUrl.Release();
        echo.Release();
        receive.Release();
        errors.Release();
        traces.Release();

        fieldsAreOk = false;
    }
    return S_OK;
}

// delete a saved packet
//
void CMyScript::PacketDelete( int sentlist, CMyScript_savedpacket_t * item )
{
    if( sentlist )
    {
        PBL_LIST_UNLINK( senthead, senttail, item, next, prev );
    }
    else
    {
        PBL_LIST_UNLINK( recvhead, recvtail, item, next, prev );
    }
    FREE( item->packetid );
    FREE( item->packet );
    FREE( item );
}

int CMyScript::PacketSave( int sentlist, char * packetid,
               char * packet, int length )
{
    CMyScript_savedpacket_t * item;

    while(( item = PacketFind( sentlist, packetid )))
    {
        PacketDelete( sentlist, item );
    }

    item = ( CMyScript_savedpacket_t * ) malloc( sizeof( *item ));
    if( !item )
    {
        // out of memory, don't save the packet
        //
        return( 0 );
    }

    item->packetid = strdup( packetid );
    if( !item->packetid )
    {
        FREE( item );
        return( 0 );
    }

    if( packet && ( length > 0 ))
    {
        item->packet = ( char * ) malloc( length );
        if( !item->packet )
        {
            FREE( item->packetid );
            FREE( item );
            return( 0 );
        }
        memcpy( item->packet, packet, length );
        item->length = length;
    }

    item->saved = time( NULL );
    item->lastsent = item->saved;
    
    if( sentlist )
    {
        PBL_LIST_APPEND( senthead, senttail, item, next, prev );
    }
    else
    {
        PBL_LIST_APPEND( recvhead, recvtail, item, next, prev );
    }
    return( 0 );
}

CMyScript_savedpacket_t * CMyScript::PacketFind( int sentlist, char * packetid )
{
    CMyScript_savedpacket_t * item;

    if( sentlist )
    {
       item = senthead;
    }
    else
    {
        item = recvhead;
    }
    for( ; item; item = item->next )
    {
        if( !strcmp( item->packetid, packetid ))
        {
            return( item );
        }
    }

    return( NULL );
}

void CMyScript::PacketsHandle()
{
    CMyScript_savedpacket_t * item;
    time_t now = time( NULL );

    if( lastPacketsHandle == now )
    {
        // don't do it again
        return;
    }
    lastPacketsHandle = now;

    // delete all packets that are expired
    //
    while(( item = senthead ))
    {
        if( item->saved + 30 < now )
        {
            PacketDelete( 1, item );
            continue;
        }
        break;
    }

    while(( item = recvhead ))
    {
        if( item->saved + 30 < now )
        {
            PacketDelete( 0, item );
            continue;
        }
        break;
    }

    // run through the packets we sent and resend the ones that need it
    //
    for( item = senthead; item; item = item->next )
    {
        if( item->lastsent + 3 >= now )
        {
            // resend it later
            //
            continue;
        }

        SendBuffer( item->packet, item->length );
        item->lastsent = now;
    }
}

// Parse a packet received
//
int CMyScript::ReceivePacket( char * packet, int length )
{
    char * arg = packet;
    char * endptr = packet + length;
    int    narg = 0;
    char * packetid = NULL;
    int    answer = -1;

    char   buffer[ 32 ];

    buffer[ 0 ] = 0;

    // find out how many arguments are in the packet
    //
    for( char * p = packet; p < endptr; p++ )
    {
        if( *p != 0 )
        {
            continue;
        }
        narg++;
    }

    // remember how many strings we will set to the output
    //
    sprintf( buffer, "%d", narg - 3 );
    narg = 0;

    // the packet consists of \0 terminated strings
    //
    for( char * ptr = packet; ptr < endptr; ptr++ )
    {
        if( *ptr != 0 )
        {
            continue;
        }

        // found the end of a word in the data received
        //
        switch( narg )
        {
          case 0:
            if( !strcmp( arg, "RQ" ))
            {
                // this is not an answer
                //
                answer = 0;
            }
            else if( !strcmp( arg, "AN" ))
            {
                // this is an answer
                //
                answer = 1;
            }
            else
            {
                // ignore packets that do not have the correct header
                return( 0 );
            }
            break;

          case 1:
            // remember the packet id
            //
            packetid = arg;
            break;

          case 2:
            if( answer == 0 )
            {
                // make sure the request has the correct connection id
                //
                if( !UdpConnectionId || strcmp( UdpConnectionId, arg ))
                {
                    // ignore packets with wrong connection id
                    //
                    return( 0 );
                }

                if( strcmp( "0", packetid ))
                {
                    // acknowledge the request
                    //
                    char buffer[ 0xff ];
                    strcpy( buffer, "AN" );
                    int len = 3;

                    snprintf( buffer + len,
                              sizeof( buffer ) - ( len + 2 ),
                              "%s", packetid );
                    len += strlen( buffer + len ) + 1;

                    snprintf( buffer + len,
                              sizeof( buffer ) - ( len + 1 ),
                              "%s", arg );
                    len += strlen( buffer + len ) + 1;

                    SendBuffer( buffer, len );

                    if( PacketFind( 0, packetid ))
                    {
                        // ignore the replay
                        //
                        return( 0 );
                    }
                    else
                    {
                        // save the packet in order to detect replays
                        //
                        PacketSave( 0, packetid, NULL, 0 );
                    }
                }
            }
            else if( answer > 0 )
            {
                CMyScript_savedpacket_t * item = NULL;

                // it is an answer from the server
                //
                item = PacketFind( 1, packetid );
                if( !item )
                {
                    // ignore the answer, we don't know the request
                    //
                    return( 0 );
                }
           
                if( !UdpConnectionId )
                {
                    UdpConnectionId = strdup( arg );
                    if( !UdpConnectionId )
                    {
                        // give back an error to the VRML
                        //
                        AppendErr( _T( "0003" ));
                        AppendErr( _T( "out of memory" ));
                        return( 0 );
                    }
                    if( strlen( UdpConnectionId ) > 8 )
                    {
                        UdpConnectionId[ 8 ] = 0;
                    }
                }

                // make sure the request has the correct connection id
                //
                if( strcmp( UdpConnectionId, arg ))
                {
                    // ignore packets with wrong connection id
                    //
                    return( 0 );
                }

                // we received an answer for a request, 
                //
                PacketDelete( 1, item );
            }
            else
            {
                // ignore the packet with the strange header
                //
                return( 0 );
            }
            break;

          case 3:
            // we give back the number of arguments that follow
            //
            AppendOut( _T( buffer ));
            AppendOut( _T( arg ));
            break;

          default:
            // we found a new string to set to the output
            //
            AppendOut( _T( arg ));
            break;
        }

        narg++;
        arg = ptr + 1;
    }

    return( 0 );
}

// Read a packet from an UDP socket, poll the socket with select
//
int CMyScript::ReceiveFromServer()
{
    char               UdpReceiveBuffer[ 32 * 1024 ];
    struct sockaddr_in from;
    int                fromlen = sizeof( from );
    int                rc;
    struct timeval     timeout;
    fd_set             fdvar;
    int                SocketError = 0;
    int                OptLength = sizeof( SocketError );

    if( UdpSocket < 0 )
    {
        // we do not handle HTTP tunneling yet
        //
        return ( -1 );
    }

    // clear error condition on the socket
    //
    OptLength = sizeof( SocketError );
    getsockopt( UdpSocket, SOL_SOCKET, SO_ERROR,
                ( char * )&SocketError, &OptLength );

    // loop until no more data is received
    //
    for(;;)
    {
        // poll with no timeout
        //
        memset( &timeout, 0, sizeof( timeout ));

        // select to see whether there is data available
        //
        FD_ZERO( &fdvar );
        FD_SET( UdpSocket, &fdvar );
        rc = select( UdpSocket + 1,
                     &fdvar,
                     ( fd_set * ) 0,
                     ( fd_set * ) 0,
                     &timeout
                   );

        switch( rc )
        {
          case 0:
            // no data available
            //
            return ( 0 );

          case -1:
            // an interrupt or something, no data available
            //
            return( 0 );
        }

        // clear error condition on the socket
        //
        OptLength = sizeof( SocketError );
        getsockopt( UdpSocket, SOL_SOCKET, SO_ERROR,
                    ( char * )&SocketError, &OptLength );

        if( SocketError )
        {
            // select returned because there was an error condition of the 
            // the socket, do it again
            //
            continue;
        }

        // allow packets from all hosts
        //
        fromlen = sizeof( from );
        memset( &from, 0, fromlen );
    
        // read the data
        //
        rc = recvfrom( UdpSocket, UdpReceiveBuffer, sizeof( UdpReceiveBuffer ),
                       0, ( struct sockaddr * )&from, &fromlen );

        if( rc < 0 )
        {
            return( 0 );
        }
        else if( rc > 2 )
        {
            char tracebuffer[ 64 ];
            snprintf( tracebuffer, sizeof( tracebuffer ),
                      "udprecv %c%c", UdpReceiveBuffer[ 0 ],
                      UdpReceiveBuffer[ 1 ] );
            AppendTra( _T( tracebuffer ));
        }

        // parse the data received and send it to the VRML
        //
        rc = ReceivePacket( UdpReceiveBuffer, rc );
        if( rc )
        {
            // ignore packet parse errors
            //
        }
    }

    return( 0 );
}

// Send a buffer
//
int CMyScript::SendBuffer( char * buffer, int len )
{
    int rc = 0;
    int SocketError = 0;
    int OptLength = sizeof( int );

    if( UdpSocket < 0 )
    {
        // we do not handle HTTP tunneling yet
        //
        return ( -1 );
    }

    // clear error condition on the socket
    //
    OptLength = sizeof( SocketError );
    getsockopt( UdpSocket, SOL_SOCKET, SO_ERROR,
                ( char * )&SocketError, &OptLength );

    rc = sendto( UdpSocket, buffer, len,
                 0, ( struct sockaddr * )UdpServerAddress,
                 UdpServerAddressLength );

    if( rc != len )
    {
        // ignore UDP send errors
        //
    }
    else if( rc > 2 )
    {
        char tracebuffer[ 64 ];
        snprintf( tracebuffer, sizeof( tracebuffer ),
                  "udpsend %c%c", buffer[ 0 ], buffer[ 1 ] );
        AppendTra( _T( tracebuffer ));
    }

    return( 0 );
}

// Send the contents of the send buffer to the server
//
int CMyScript::SendToServer()
{
    int rc = 0;

    if( UdpSendBufferLength < 1 )
    {
        return( 0 );
    }

    rc = SendBuffer( UdpSendBuffer, UdpSendBufferLength );
    if( rc != UdpSendBufferLength )
    {
        // ignore UDP send errors
        //
    }

    // save the packet for resend if no acknowledgement arrives
    //
    PacketSave( 1, UdpSendBuffer + strlen( UdpSendBuffer ) + 1,
                UdpSendBuffer, UdpSendBufferLength );

    UdpSendBufferLength = 0;
    return( 0 );
}

// Set string to the send buffer
//
int CMyScript::SetSendString( const char * s )
{
    if( !s )
    {
        s = "";
    }

    int length = strlen( s ) + 1;

    // make sure the send buffer is big enough for the string
    //
    while( UdpSendBufferLength + 22 + length >= UdpSendBufferSize )
    {
        // we allow 32 k maximum send buffer length
        //
        if( UdpSendBufferLength + 22 + length >= 32 * 1024 )
        {
            // give back an error to the VRML
            //
            AppendErr( _T( "0005" ));
            AppendErr( _T( "send buffer overflow" ));
            return( 0 );
        }

        char * b = (char*)realloc( UdpSendBuffer,
                                   UdpSendBufferSize + 8 * 1024 );
        if( !b )
        {
            // give back an error to the VRML
            //
            AppendErr( _T( "0003" ));
            AppendErr( _T( "out of memory" ));
            return( 0 );
        }
        UdpSendBuffer = b;
        UdpSendBufferSize += 8 * 1024;
    }

    if( UdpSendBufferLength == 0 )
    {
        // if the request to send is an ENTER request,
        // we reset the packet and the connection id
        //
        if( !strcmp( "ENTER", s ))
        {
            srand( time( 0 ) ^ (( rand() << 15 ) ^ rand()));
            UdpPacketId = ( rand() << 15 ) ^ rand();

            FREE( UdpConnectionId );
        }

        // a new packet gets sent, all packets start with RQ
        //
        strcpy( UdpSendBuffer, "RQ" );
        UdpSendBufferLength = 3;

        // create a new packetid
        //
        while( ++UdpPacketId > 0x7fffffff )
        {
            UdpPacketId = ( rand() << 15 ) ^ rand();
        }
        sprintf( UdpSendBuffer + UdpSendBufferLength, "%0lx", UdpPacketId );
        UdpSendBufferLength += 1 + strlen( UdpSendBuffer + UdpSendBufferLength);

        // each packet sent contains the connection ID
        //
        if( UdpConnectionId )
        {
            strcpy( UdpSendBuffer + UdpSendBufferLength, UdpConnectionId );
        }
        else
        {
            // start with an empty connection id
            //
            strcpy( UdpSendBuffer + UdpSendBufferLength, "0" );
        }

        UdpSendBufferLength += 1 + strlen( UdpSendBuffer + UdpSendBufferLength);
    }

    // copy the data to the buffer
    //
    memcpy( UdpSendBuffer + UdpSendBufferLength, s, length );
    UdpSendBufferLength += length;

    return( 0 );
}

// Fill a srvadr structure for the internet
//
// ip and port have to be given in host byte order
//
int CMyScript::GetServerAddress( unsigned long ip, int port, char ** s )
{
    struct sockaddr_in *adr;

    adr = (struct sockaddr_in *)malloc( sizeof( struct sockaddr_in ));
    if( !adr )
    {
        return( -1 );
    }

    adr->sin_family = AF_INET;
    adr->sin_port   = htons( ( short )port );
    ip            = htonl( ip );
    memcpy( &( adr->sin_addr.s_addr ),
            &ip,
            sizeof( adr->sin_addr.s_addr )
          );

    *s = ( char * )adr;
    return( sizeof( struct sockaddr_in ) );
}

// Get the ip address in host format from host.domain or xxx.xxx.xxx.xxx
//
// @return    int rc >= 0: Call went ok; ip address returned
// @return    int rc <    0: An error occured
//
int CMyScript::GetHostByName( char * hostname, unsigned long * ip )
{
    struct hostent  * hostinfo = NULL;

    // NT reports an error in gethostbyname with a dotted ip address
    //
    if( isdigit( *hostname ))
    {
        *ip = inet_addr( hostname );
        if( *ip )
        {
            *ip = ntohl( *ip );
            return( 0 );
        }
    }

    // call the host name lookup function in a loop,
    // it might fail of just called once
    //
    for( int i = 0; i < 10; i++ )
    {
        hostinfo = gethostbyname( hostname );
        if( !hostinfo )
        {
            if( WSAGetLastError( ) == WSATRY_AGAIN )
            {
                continue;
            }
        }

        break;
    }

    if( ! hostinfo )
    {
        return( -1 );
    }

    memcpy( ip, hostinfo->h_addr, sizeof( unsigned long ) );
    *ip = ntohl( *ip );

    return( 0 );
}


int CMyScript::ConnectionInit( )
{
    // protect against bad , missing fields
    //
    if (!fieldsAreOk)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    // get the number of strings
    //
    int cnt = 0;
    hr = serverUrl->getSize( &cnt );

    // malloc the server url list
    //
    FREE( ServerUrlList );
    ServerUrlList = (BSTR*) calloc( cnt, sizeof( BSTR ));

    // query value
    //
    hr = serverUrl->getValue( cnt, ServerUrlList );

    //test low level output
    USES_CONVERSION ;

    LPCTSTR url = NULL;

    // loop through the server urls
    //
    for( int i = 0; i < cnt; i++, ServerUrlToUse++ )
    {
        if( ServerUrlToUse >= cnt )
        {
            ServerUrlToUse = 0;
        }

        // convert to local string type (TCHAR)
        //
        LPCTSTR url;
        url = W2T( ServerUrlList[ ServerUrlToUse ] );

        if( !url )
        {
            return E_POINTER;
        }

        // parse the different server urls
        //
        char * udphead = "udp://";

        // open a udp connection
        //
        if( !strncmp( url, udphead, strlen( udphead )))
        {
            // parse the server name and the port from the url
            //
            char * hostname = strdup( url + strlen( udphead ) );
            char * ptr = strchr( hostname, ':' );
            int    port = 1510;

            if( ptr )
            {
                *ptr++ = 0;
                port = atoi( ptr );
            }
            else if(( ptr = strchr( hostname, '/' )))
            {
                *ptr = 0;
            }

            // lookup the server name
            //
            int rc = GetHostByName( hostname, &UdpServerIp );
            if( rc < 0 )
            {
                // give back an error to the VRML
                //
                AppendErr( _T( "0002" ));
                AppendErr( _T( "gethostbyname failed" ));
                AppendErr( _T( "hostname" ));
                FREE( hostname );
                return( hr );
            }
            FREE( hostname );

            // Create the server address structure
            //
            FREE( UdpServerAddress );
            rc = GetServerAddress( UdpServerIp, port, &UdpServerAddress );
            if( rc < 0 )
            {
                // give back an error to the VRML
                //
                AppendErr( _T( "0003" ));
                AppendErr( _T( "out of memory" ));
                return( hr );
            }
            UdpServerAddressLength = rc;

            // Open the client socket
            //
            if( UdpSocket >= 0 )
            {
                closesocket( UdpSocket );
                UdpSocket = -1;
            }

            UdpSocket = socket( AF_INET, SOCK_DGRAM, 0 );
            if( UdpSocket == INVALID_SOCKET )
            {
                // give back an error to the VRML
                //
                AppendErr( _T( "0004" ));
                AppendErr( _T( "failed to create udp socket" ));
                return( hr );
            }

            return( hr );
        }
    }

    return( hr );
}

STDMETHODIMP CMyScript::loadScriptObject(BSTR urlData)
{
    //
    return E_NOTIMPL;
}

STDMETHODIMP CMyScript::initialize()
{
    // protect against bad , missing fields
    if (!fieldsAreOk)
        return E_POINTER;

    // test
    AppendTra( _T( "initialize" ));

    return S_OK;

}

STDMETHODIMP CMyScript::shutdown()
{
    // cleanup stuff done in initialize
    // test
    AppendTra( _T( "shutdown" ));

    return S_OK;
}

STDMETHODIMP CMyScript::processEvent(
BSTR name,   // name of eventIn function
INT eventId, // ID, , numbered by definition order (+3) for built-in fields
EventOut * value,    // the value
DOUBLE timeStamp
)
{
    HRESULT hr = S_OK;

    if (!fieldsAreOk)
        return E_POINTER;

    if (wcscmp(name,L"send")  == 0) {

        //  the send eventIn function
        CComQIPtr<EventOutMFString> send(value);

        if (!send) // bad, not an MFString
            return E_POINTER;

        AppendTra( _T( "send" ));

        // get the number of strings
        int cnt = 0;
        hr = send->getSize( &cnt );

        // the array of strings
        BSTR* value=NULL;

        value = (BSTR*) calloc(cnt,sizeof(BSTR));

        //query value
        hr=send->getValue(cnt,value);

        // process the string

        //
        //echo
        //
        VARIANT_BOOL flag;
        hr = echo->getValue(&flag);
        if (flag) {

            //test low level output
            USES_CONVERSION ;

            int n = 0;
            for( int i = 0; i < cnt; i++ )
            {
                // convert to local string type (TCHAR)
                //
                LPCTSTR t;
                t = W2T( value[ i ]);

                if( n-- <= 0 )
                {
                    if( i > 0 )
                    {
                        // send to the server
                        SendToServer();
                    }
                    n = atoi( t );
                    continue;
                }
                    
                // put in out network queue
                //
                SetSendString(t);
                AppendTra( t );
            }

            // send to the server
            SendToServer();
        }

        // free the strings , alloctated by getValue
        FreeBSTRs( &value, &cnt );

    }
    else if (wcscmp(name,L"timer")    == 0)
    {
        //AppendTra( _T( "timer" ));

        // handle packets saved for replay
        PacketsHandle();
    }
    else {
        // we don't know what to do with this event
    }



    return S_OK;
}

STDMETHODIMP CMyScript::eventsProcessed()
{
    // receive data from the server
    //
    ReceiveFromServer();

    // if some data to be sent to the server was received, we send it
    //
    if( UdpSendBufferLength > 0 )
    {
        SendToServer();
    }

    // send trace strings
    if( hasTraValue()) {
        traces->setValue( traValueCnt, traValue );
        // free the strings
        FreeBSTRArray( traValue, traValueCnt );
        traValueCnt=0;
    }

    // send error strings
    if( hasErrValue()) {
        errors->setValue( errValueCnt, errValue );
        // free the strings
        FreeBSTRArray( errValue, errValueCnt );
        errValueCnt=0;
    }

    // if no error, send outgoing strings
    else if( hasOutValue()) {
        receive->setValue( outValueCnt, outValue );
        // free the strings
        FreeBSTRArray( outValue, outValueCnt );
        outValueCnt=0;
    }

    // could do the work here, depending on which event has been changed
    return S_OK;
}
