/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../maslib/mas_api_internal.h"
#include "net_common.h"

#define DEFAULT_PACKAGE_SIZE 1024

/** LOCAL PROTOTYPES *****************************************************/


/** EXPORTED FUNCTIONS ***************************************************/

int32
mas_net_connect_control( mas_device_t device, char* hostname,
                         mas_port_t *source, mas_port_t *sink )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    uint32 ssrc;

    ssrc = _get_ssrc();

    masc_setup_event( &event );
    event.action_name = "mas_net_connect";
    event.device_instance = device->device_instance;
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "t", MAS_CHANNEL_TYPE_CONTROL );
    masc_pushk_uint32( &ppkg, "ssrc", ssrc );
    masc_pushk_string( &ppkg, "hn", hostname );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &ppkg );
    
    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* detect error condition */
    if ( masc_test_key( &rpkg, "err" ) == 0 )
    {
        /* device sets derror to 1 if it thought it was biting its own
           tail */
        masc_pullk_int32( &rpkg, "err", &err );
        goto done;
    }
    
    /* otherwise, grab the ports */
    if ( ( err = _alloc_port( source ) ) < 0 ) goto done;
    if ( ( err = _alloc_port( sink ) ) < 0 ) goto done;
    masc_pullk_int32( &rpkg, "lsrc", &((*source)->portnum) );
    masc_pullk_int32( &rpkg, "lsnk", &((*sink)->portnum) );

    err = _alloc_channel( &((*source)->control_channel) );
    if ( err < 0 ) goto done;
    err = _alloc_channel( &((*sink)->control_channel) );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*source)->control_channel, device->control_channel );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*sink)->control_channel, device->control_channel );
    if ( err < 0 ) goto done;

    err = 0;
 done:
    masc_strike_package( &rpkg );
    return err;
}

/* you must have a control connection open to the remote side to do
 * this */ 
int32
mas_net_connect_data( mas_device_t device, mas_channel_t remote,
                      char* channel_name, mas_port_t *source1,
                      mas_port_t *sink1, mas_port_t* source2,
                      mas_port_t* sink2 )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    uint32 ssrc;

    ssrc = _get_ssrc();

    masc_setup_event( &event );
    event.action_name = "mas_net_connect";
    event.device_instance = device->device_instance;
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "t", MAS_CHANNEL_TYPE_DATA );
    masc_pushk_uint32( &ppkg, "ssrc", ssrc );
    masc_pushk_string( &ppkg, "hn", remote->hostname );
    masc_pushk_string( &ppkg, "cn", channel_name );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &ppkg );
    
    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* detect error condition */
    if ( masc_test_key( &rpkg, "err" ) == 0 )
    {
        /* device sets derror to 1 if it thought it was biting its own
           tail */
        masc_pullk_int32( &rpkg, "err", &err );
        goto done;
    }
    
    /* grab the ports */
    if ( ( err = _alloc_port( source1 ) ) < 0 ) goto done;
    if ( ( err = _alloc_port( sink1 ) ) < 0 ) goto done;
    if ( ( err = _alloc_port( source2 ) ) < 0 ) goto done;
    if ( ( err = _alloc_port( sink2 ) ) < 0 ) goto done;
    masc_pullk_int32( &rpkg, "lsrc", &((*source1)->portnum) );
    masc_pullk_int32( &rpkg, "lsnk", &((*sink1)->portnum) );
    masc_pullk_int32( &rpkg, "rsrc", &((*source2)->portnum) );
    masc_pullk_int32( &rpkg, "rsnk", &((*sink2)->portnum) );

    /* Set the control channels.  The latter two are on the remote
       side of the connection. */
    err = _alloc_channel( &((*source1)->control_channel) );
    if ( err < 0 ) goto done;
    err = _alloc_channel( &((*sink1)->control_channel) );
    if ( err < 0 ) goto done;
    err = _alloc_channel( &((*source2)->control_channel) );
    if ( err < 0 ) goto done;
    err = _alloc_channel( &((*sink2)->control_channel) );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*source1)->control_channel, device->control_channel );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*sink1)->control_channel, device->control_channel );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*source2)->control_channel, remote );
    if ( err < 0 ) goto done;
    err = _copy_channel( (*sink2)->control_channel, remote );
    if ( err < 0 ) goto done;

    err = 0;
 done:
    masc_strike_package( &rpkg );
    return err;
}
