/*
 * 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 <unistd.h>
#include <string.h>
#include <gtk/gtk.h>
#include "mas/mas.h"
#include "mas/mas_core.h"
#include "mas/mas_mix.h"

#define VOL_INTERVALS 5

#define SPCG 6
#define PADG 12
#define BRDR 0

/* some global widgets (I think that's clearer than g_object_set_data) */
static GtkWidget *ip_box;
static GtkWidget *connect_btn;
static GtkWidget *mic_on_btn;
static GtkWidget *mic_off_btn;
static GtkWidget *vol_up;
static GtkWidget *vol_dn;
static GtkWidget *set_btn;
static GtkWidget *sbuf_spin;
static GObject   *adj;


static mas_device_t anx;
static mas_device_t sbuf;
static mas_device_t display_side_mix;
static mas_port_t display_side_mix_sink;

static int connected=0;
static int mic_on=1;
static int mic_toggle_hack = 0;
static char *remote_ip = 0;
static int volume = VOL_INTERVALS;
static int sink_set = 0;


/* GUI callbacks */
static gint cb_delete_event( GtkWidget *w, GdkEvent *e, gpointer o );
static gint cb_connect_btn( GtkWidget *w, gpointer data );
static gint cb_mic_btn( GtkWidget *w, gpointer data );
static gint cb_vol( GtkWidget *w, gpointer data );
static gint cb_buftime( GtkWidget *w, gpointer data );


/* local yokels */
static void usage( void );
static void mas_path_setup( void );
static void gui_setup( int *argc, char **argv[] );



static void
usage( void )
{
    fprintf(stderr, "usage: masconf [-c] [hostname]\n  -c: console (non-GUI) mode\n");
    return;
}


static void
mas_path_setup( void )
{
    int32 err;
    struct mas_data_characteristic* dc;
    mas_port_t    anx_source;
    mas_device_t  rmix, lcodec, rcodec, lch, rch, lsr, rsr, frag;
    mas_channel_t peer;
    mas_port_t    mix_sink, lc_snk, lc_src, rc_snk, rc_src;
    mas_port_t    lch_snk, lch_src, rch_snk, rch_src;
    mas_port_t    lsr_snk, lsr_src, rsr_snk, rsr_src;
    mas_port_t    sbuf_snk, sbuf_src;
    mas_port_t    frag_snk, frag_src;
    char newrate[] = "8000";
    char newchannels[] = "1";
    char oldrate[] = "44100";
    char oldchannels[] = "2";
    struct mas_package nugget;
        
    /** get the display-side anx device - we need the mic output */
    err = mas_asm_get_device_by_name( "anx", &anx );
    if ( err < 0 ) masc_logerror( err, "get anx" );
    
    err = mas_asm_get_port_by_name( anx, "audio_source", &anx_source );
    if ( err < 0 ) masc_logerror( err, "get anx source" );
    
    /** make a channel to the destination (peer) MAS */
    err = mas_make_control_channel( remote_ip, &peer );
    if ( err < 0 ) masc_logerror( err, "couldn't connect to peer at %s",
                                  remote_ip );
    
    /** get his mix device so we can send our audio over there */
    err = mas_asm_get_device_by_name_on_channel( "mix", &rmix, peer );
    if ( err < 0 ) masc_logerror( err, "get remote mix" );
    
    err = mas_asm_get_port_by_name( rmix, "default_mix_sink", &mix_sink );
    if ( err < 0 ) masc_logerror( err, "get mix sink" );

    
    /** make a ulaw CODEC on the display side and get its ports */
    err = mas_asm_instantiate_device( "codec_ulaw", 0, 0, &lcodec );
    if ( err < 0 ) masc_logerror( err, "instantiating ULAW codec, locally" );
    
    err = mas_asm_get_port_by_name( lcodec, "source", &lc_src );
    if ( err < 0 ) masc_logerror( err, "get local codec source" );

    err = mas_asm_get_port_by_name( lcodec, "sink", &lc_snk );
    if ( err < 0 ) masc_logerror( err, "get local codec sink" );

    /** make a ulaw CODEC on the other side and get its ports */
    err = mas_asm_instantiate_device_on_channel( "codec_ulaw", 0, 0, &rcodec, peer ); 
    if ( err < 0 ) masc_logerror( err, "instantiating ULAW codec, remotely" );
    
    err = mas_asm_get_port_by_name( rcodec, "source", &rc_src );
    if ( err < 0 ) masc_logerror( err, "get remote codec source" );

    err = mas_asm_get_port_by_name( rcodec, "sink", &rc_snk );
    if ( err < 0 ) masc_logerror( err, "get remote codec sink" );

    /** make a channel converter on the display side and get its ports */
    err = mas_asm_instantiate_device( "channelconv", 0, 0, &lch );
    if ( err < 0 ) masc_logerror( err, "instantiating channelconv, locally" );
    
    err = mas_asm_get_port_by_name( lch, "source", &lch_src );
    if ( err < 0 ) masc_logerror( err, "get local cc source" );

    err = mas_asm_get_port_by_name( lch, "sink", &lch_snk );
    if ( err < 0 ) masc_logerror( err, "get local cc sink" );

    /** make a channel converter on the other side and get its ports */
    err = mas_asm_instantiate_device_on_channel( "channelconv", 0, 0, &rch, peer ); 
    if ( err < 0 ) masc_logerror( err, "instantiating channelconv, remotely" );
    
    err = mas_asm_get_port_by_name( rch, "source", &rch_src );
    if ( err < 0 ) masc_logerror( err, "get remote cc source" );

    err = mas_asm_get_port_by_name( rch, "sink", &rch_snk );
    if ( err < 0 ) masc_logerror( err, "get remote cc sink" );

    /** make a sample rate converter on the display side and get its ports */
    err = mas_asm_instantiate_device( "srate", 0, 0, &lsr );
    if ( err < 0 ) masc_logerror( err, "instantiating srate, locally" );
    
    err = mas_asm_get_port_by_name( lsr, "source", &lsr_src );
    if ( err < 0 ) masc_logerror( err, "get local srate source" );

    err = mas_asm_get_port_by_name( lsr, "sink", &lsr_snk );
    if ( err < 0 ) masc_logerror( err, "get local srate sink" );

    /** make a channel converter on the other side and get its ports */
    err = mas_asm_instantiate_device_on_channel( "srate", 0, 0, &rsr, peer ); 
    if ( err < 0 ) masc_logerror( err, "instantiating srate, remotely" );
    
    err = mas_asm_get_port_by_name( rsr, "source", &rsr_src );
    if ( err < 0 ) masc_logerror( err, "get remote srate source" );

    err = mas_asm_get_port_by_name( rsr, "sink", &rsr_snk );
    if ( err < 0 ) masc_logerror( err, "get remote srate sink" );

    /* create the sbuf and get its sink & source */
     err = mas_asm_instantiate_device_on_channel( "sbuf", 0, 0, &sbuf, peer );
    if ( err < 0 ) masc_logerror( err, "instantiate sbuf" );

    err = mas_asm_get_port_by_name( sbuf, "source", &sbuf_src );
    if ( err < 0 ) masc_logerror( err, "get sbuf source" );

    err = mas_asm_get_port_by_name( sbuf, "sink", &sbuf_snk );
    if ( err < 0 ) masc_logerror( err, "get sbuf sink" );

    /* create the frag and get its sink & source */
    err = mas_asm_instantiate_device_on_channel( "frag", 0, 0, &frag, peer );
    if ( err < 0 ) masc_logerror( err, "instantiate frag" );

    err = mas_asm_get_port_by_name( frag, "source", &frag_src );
    if ( err < 0 ) masc_logerror( err, "get frag source" );

    err = mas_asm_get_port_by_name( frag, "sink", &frag_snk );
    if ( err < 0 ) masc_logerror( err, "get frag sink" );

    /* connect in order from display anx source all the way to the
       remote mix sink:

       anx->channelconv->srate->ulaw->net ...
                        net->ulaw->srate->channelconv->anx

       notes:
         By converting to mono first, the srate conversion will
         go faster.

    */

    err = mas_asm_connect_ports( anx_source, lch_snk );
    if ( err < 0 ) masc_logerror( err, "connect anx source to channelconv sink" );

    /* channelconvert to mono */
    mas_asm_get_dc( anx_source, &dc );
    err = masc_get_index_of_key( dc, "channels" );
    if ( err < 0 )
    {
        masc_logerror( err, "badness getting dc" );
        exit;
    }
    
    masc_rtfree( dc->values[err] );
    dc->values[err] = masc_rtalloc( strlen(newchannels) + 1 );
    strcpy( dc->values[err], newchannels );
    err = mas_asm_connect_source_sink( lch_src, lsr_snk, dc );
    if ( err < 0 ) masc_logerror( err, "connect channelconv source to srate sink" );
    /* change sampling rate to 'newrate' */
    err = masc_get_index_of_key( dc, "sampling rate" );
    if ( err < 0 )
    {
        masc_logerror( err, "badness getting dc" );
        exit;
    }
    
    masc_rtfree( dc->values[err] );
    dc->values[err] = masc_rtalloc( strlen(newrate) + 1 );
    strcpy( dc->values[err], newrate );
    err = mas_asm_connect_source_sink( lsr_src, lc_snk, dc );
    if ( err < 0 ) masc_logerror( err, "connect srate source to ulaw sink" );

    
    /* connect across the network */
    err = mas_asm_connect_ports( lc_src, rc_snk );
    if ( err < 0 ) masc_logerror( err, "connect codec to codec" );
    
    err = mas_asm_connect_ports( rc_src, rsr_snk );
    if ( err < 0 ) masc_logerror( err, "connect codec to srate sink" );
    
    /* change the sampling rate to 'oldrate' */
    err = masc_get_index_of_key( dc, "sampling rate" );
    if ( err < 0 )
    {
        masc_logerror( err, "badness getting dc" );
        exit;
    }

    masc_rtfree( dc->values[err] );
    dc->values[err] = masc_rtalloc( strlen(oldrate) + 1 );
    strcpy( dc->values[err], oldrate );
    err = mas_asm_connect_source_sink( rsr_src, rch_snk, dc );
    if ( err < 0 ) masc_logerror( err,
                                  "connect srate source to channelconv sink" );

    /* change back to stereo */
    err = masc_get_index_of_key( dc, "channels" );
    if ( err < 0 )
    {
        masc_logerror( err, "badness getting dc" );
        exit;
    }
    
    masc_rtfree( dc->values[err] );
    dc->values[err] = masc_rtalloc( strlen(oldchannels) + 1 );
    strcpy( dc->values[err], oldchannels );

    err = mas_asm_connect_source_sink( rch_src, frag_snk, dc );
    if ( err < 0 ) masc_logerror( err, "connect channelconv to frag sink" );

    err = mas_asm_connect_source_sink( frag_src, sbuf_snk, dc );
    if ( err < 0 ) masc_logerror( err, "connect frag to sbuf sink" );

#ifdef USE_VISUAL
    {
        mas_device_t vis;
        mas_port_t vis_snk, vis_src;
        
        /* create the visual and get its sink & source */
        err = mas_asm_instantiate_device_on_channel( "visual", 0, 0, &vis, peer );
        if ( err < 0 ) masc_logerror( err, "instantiate visual" );
        
        err = mas_asm_get_port_by_name( vis, "source", &vis_src );
        if ( err < 0 ) masc_logerror( err, "get visual source" );
        
        err = mas_asm_get_port_by_name( vis, "sink", &vis_snk );
        if ( err < 0 ) masc_logerror( err, "get visual sink" );
        
        err = mas_asm_connect_ports( sbuf_src, vis_snk );
        if ( err < 0 ) masc_logerror( err, "connect sbuf source to visual sink" );
        masc_make_dc( &dc, 6 );
        masc_append_dc_key_value( dc, "format", "linear" );
        masc_append_dc_key_value( dc, "resolution", "16" );
        masc_append_dc_key_value( dc, "sampling rate", "44100" );
        masc_append_dc_key_value( dc, "channels", "2" );
        masc_append_dc_key_value( dc, "endian", "host" );
        err = mas_asm_connect_source_sink( vis_src, mix_sink, dc );
        if ( err < 0 ) masc_logerror( err, "connect visual source to mix sink" );

        {
            /* configure the visual device */
            struct mas_package pkg;
            char pbuf[1024];

            masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
            masc_pushk_int16( &pkg, "n_bins", 50 );
            masc_finalize_package( &pkg );
            mas_set( vis, "n_bins", &pkg );
            masc_strike_package( &pkg );
            
            masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
            masc_pushk_int16( &pkg, "spikes", 0 );
            masc_finalize_package( &pkg );
            mas_set( vis, "spikes", &pkg );
            masc_strike_package( &pkg );

            masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
            masc_pushk_int16( &pkg, "scale_cutoff", -80 );
            masc_finalize_package( &pkg );
            mas_set( vis, "scale_cutoff", &pkg );
            masc_strike_package( &pkg );

            masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
            masc_pushk_int16( &pkg, "do_work", 1 );
            masc_finalize_package( &pkg );
            mas_set( vis, "do_work", &pkg );
            masc_reset_package( &pkg );
            masc_strike_package( &pkg );
        }
    }
#endif
#ifndef USE_VISUAL
    /* alternative to this is the commented out block above */
    err = mas_asm_connect_ports( sbuf_src, mix_sink );
    if ( err < 0 ) masc_logerror( err, "connect sbuf source to mix sink" );
#endif
    
    /* set buffer time */
    masc_setup_package( &nugget, NULL, 0, 0 );
    masc_pushk_uint32( &nugget, "buftime_ms", 100 );
    masc_finalize_package( &nugget );
    mas_set( sbuf, "buftime_ms", &nugget );
    masc_strike_package( &nugget );
    
    /* rename his port so that he knows where to change volume */
    mas_mix_set_portname( rmix, mix_sink, "masconf_sink" );

    mas_asm_get_device_by_name( "mix", &display_side_mix );
}



int main(int argc, char* argv[])
{    
    int graphical = TRUE;
    int parse_err = 0;
    char c;
    int32 err;
    
    masc_log_verbosity( MAS_VERBLVL_DEBUG ); 
    
    while ( ( c = getopt( argc, argv, "-c" ) ) != EOF )
    {
        switch (c)
        {
        case 'c':
            graphical = FALSE;
            break;

        case 1:
            remote_ip = strdup ( optarg );
            break;
            
      default:
          parse_err++;
          break;
        }
    }

    if ( parse_err )
    {
        usage();
        exit(2);
    }

    if( !remote_ip )
        remote_ip = strdup( "localhost" );
    
    
    err = mas_init();
    if (err < 0)
    {
	printf("\nconnection with server failed.\n");
	exit(1);
    }



    if( graphical )
    {
        gui_setup( &argc, &argv );
        gtk_main();
        exit(0);
    }

    /* not in gui mode */

    
    mas_path_setup();

    
    /* Now that it's all set up, trigger recording. */
    err = mas_anx_record_start( anx );
    if ( err < 0 ) masc_logerror( err, "can't start recording" );
    err = mas_source_play( sbuf );
    if ( err < 0 ) masc_logerror( err, "can't start sbuf" );
    
    masc_log_message( 0, "Mic is ON" );
    
    while ( 1 )
    {
        getchar();
        err = mas_anx_record_stop( anx );
        if ( err < 0 ) masc_logerror( err, "can't stop recording" );
        err = mas_source_stop( sbuf );
        if ( err < 0 ) masc_logerror( err, "can't stop sbuf" );
        masc_log_message( 0, "Mic is OFF" );
        getchar();
        err = mas_anx_record_start( anx );
        if ( err < 0 ) masc_logerror( err, "can't start recording" );
        err = mas_source_play( sbuf );
        if ( err < 0 ) masc_logerror( err, "can't start sbuf" );
        masc_log_message( 0, "Mic is ON" );
    }
    
    exit(0);
}



gint
cb_delete_event(GtkWidget *w, GdkEvent *e, gpointer o)
{
    gtk_widget_hide_all( GTK_WIDGET(o) );
    gtk_main_quit();
    return TRUE;
}

gint
cb_quit(GtkWidget *w, gpointer o)
{
    gtk_widget_hide_all( GTK_WIDGET(o) );
    gtk_main_quit();
    return TRUE;
}


static gint
cb_connect_btn( GtkWidget *w, gpointer data )
{
    int32 err;
    
    g_free( remote_ip );
    remote_ip = g_strdup( gtk_entry_get_text(GTK_ENTRY(ip_box)) );

    mas_path_setup();

    connected = 1;

    if( mic_on )
    {
        err = mas_anx_record_start( anx );
        if ( err < 0 ) masc_logerror( err, "can't start recording" );
        err = mas_source_play( sbuf );
        if ( err < 0 ) masc_logerror( err, "can't start sbuf" );
    }
    
/*     sprintf( s, "connected to %s", remote_ip ); */
/*     gtk_label_set_text( GTK_LABEL(connect_status), s ); */
    
    gtk_widget_set_sensitive( connect_btn, FALSE );
    gtk_widget_set_sensitive( ip_box, FALSE );

    gtk_widget_set_sensitive( vol_dn, TRUE );
    gtk_widget_set_sensitive( mic_on_btn, TRUE );
    gtk_widget_set_sensitive( mic_off_btn, TRUE );
    gtk_widget_set_sensitive( set_btn, TRUE );
    gtk_widget_set_sensitive( sbuf_spin, TRUE );

    return TRUE;
}


/* static gint */
/* cb_off_btn( GtkWidget *w, gpointer data ) */
/* { */
/*     mic_on = 2; */
/*     gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_on_btn), FALSE ); */
/*     return TRUE; */
/* } */

static gint
cb_mic_btn( GtkWidget *w, gpointer data )
{
    int32 err;
    
    if( connected )
    {
        if( w==mic_on_btn )
        {
            if( mic_toggle_hack==0 )
            {
                mic_on          = 1;
                mic_toggle_hack = 1;
                if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mic_on_btn)) )
                {
                    mic_toggle_hack = 1;
                    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_off_btn), FALSE );
                }
                else
                {
                    mic_toggle_hack = 1;
                    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_on_btn), TRUE );
                }
            }
            else
            {
                mic_toggle_hack = 0;
                return TRUE;
            }
        }
        else
        {
            /* it must be the "off" button */
            if( mic_toggle_hack==0 )
            {
                mic_on          = 0;
                mic_toggle_hack = 1;
                if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mic_off_btn)) )
                {
                    mic_toggle_hack = 1;
                    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_on_btn), FALSE );
                }
                else
                {
                    mic_toggle_hack = 1;
                    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_off_btn), TRUE );
                }
            }
            else
            {
                mic_toggle_hack = 0;
                return TRUE;
            }
        }
        
        /* mic_on = 1-mic_on; */

        if( mic_on )
        {
            err = mas_anx_record_start( anx );
            if ( err < 0 )
            {
                masc_logerror( err, "can't start recording" );
            }
            else
            {
/*                 gtk_label_set_text( GTK_LABEL(mic_status), on_string ); */
            }
            err = mas_source_play( sbuf );
            if ( err < 0 ) masc_logerror( err, "can't start sbuf" );
        }
        else
        {
            err = mas_anx_record_stop( anx );
            if ( err < 0 )
            {
                masc_logerror( err, "can't stop recording" );
            }
            else
            {
/*                 gtk_label_set_text( GTK_LABEL(mic_status), off_string ); */
            }   
            err = mas_source_stop( sbuf );
            if ( err < 0 ) masc_logerror( err, "can't stop sbuf" );
        }
    }
    return TRUE;    
}


static gint
cb_vol( GtkWidget *w, gpointer data )
{
    int32 err;

    if( connected ) /* if we're not, we don't have the disp side mix
                       device and channel yet... */
    {    
        if( w==vol_up )
        {
            ++volume;
            gtk_widget_set_sensitive( vol_dn, TRUE );
            if ( volume>=VOL_INTERVALS )
            {
                volume=VOL_INTERVALS;
                gtk_widget_set_sensitive( vol_up, FALSE );
            }
        }
        else
        {
            --volume;
            gtk_widget_set_sensitive( vol_up, TRUE );
            if( volume<=0 )
            {
                volume=0;
                gtk_widget_set_sensitive( vol_dn, FALSE );
            }   
        }
        
        if( !sink_set )
        {
            err = mas_asm_get_port_by_name( display_side_mix,
                                            "masconf_sink",
                                            &display_side_mix_sink );
            if( err >= 0 )
                sink_set = 1;
        }
        
        if( sink_set )
        {
            mas_mix_attenuate_linear( display_side_mix, display_side_mix_sink,
                                      (double)volume/(double)VOL_INTERVALS );
        }
    }
    
    return TRUE;    
}


static gint
cb_buftime( GtkWidget *w, gpointer data )
{
    int number;
    char pbuf[1024];
    struct mas_package package;

    if( connected ) 
    {
        number = gtk_adjustment_get_value( GTK_ADJUSTMENT(adj) );
/*         printf( "you selected %d\n", number ); */

        masc_setup_package( &package, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
        masc_pushk_uint32( &package, "buftime_ms", number );
        masc_finalize_package( &package );
        mas_set( sbuf, "buftime_ms", &package );
        masc_strike_package( &package );

        /* make our change have immediate effect */
        mas_source_stop( sbuf );
        mas_source_play( sbuf );
        
    }
    return TRUE;    
}

static void
gui_setup( int *argc, char **argv[] )
{
    GtkWidget *main_window;
    GtkWidget *widgy;
    GtkWidget *thing;
    GtkWidget *vbox;
    GtkWidget *hbox;
    char *title;
    
    gtk_init( argc, argv );

    /* main window */
    main_window = gtk_window_new( GTK_WINDOW_TOPLEVEL );

    title = masc_construct_title( "MAS Conf");
    if( title )
    {
        gtk_window_set_title ( GTK_WINDOW (main_window), title );
        masc_rtfree( title );
    }
    else
    {
        gtk_window_set_title ( GTK_WINDOW (main_window), "MAS Conf" );
    }
    
    
    gtk_window_set_policy( GTK_WINDOW(main_window), TRUE, TRUE, TRUE );  
    
    gtk_signal_connect( GTK_OBJECT(main_window), "delete_event", 
                        GTK_SIGNAL_FUNC(cb_delete_event),
                        (gpointer)main_window );

    
    /* widgets */

    vbox = gtk_vbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(vbox), 12 );
    /*     vbox = gtk_vbox_new ( TRUE, SPCG ); */

    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    widgy = gtk_label_new("");
    gtk_label_set_markup( GTK_LABEL(widgy), "<span weight='bold'>\nConnect To</span>" );
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, FALSE, 0 );
    gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 );
    
    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );
    
    widgy = gtk_label_new("Host name or IP:");
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, TRUE, PADG );
    
    ip_box = gtk_entry_new();
    gtk_entry_set_text( GTK_ENTRY(ip_box), remote_ip );
    gtk_entry_set_width_chars( ip_box, 10 );
    
    gtk_box_pack_start( GTK_BOX(hbox), ip_box, TRUE, TRUE, 0 );
    
    connect_btn = gtk_button_new_with_label("connect");
    gtk_box_pack_start( GTK_BOX(hbox), connect_btn, TRUE, TRUE, PADG );
    
    g_signal_connect( G_OBJECT(connect_btn), "clicked",
                      G_CALLBACK (cb_connect_btn), NULL );
    
    
    gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0 );
    
/*     widgy = gtk_hseparator_new (); */
/*     gtk_box_pack_start( GTK_BOX(vbox), widgy, TRUE, TRUE, 0 ); */


    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    widgy = gtk_label_new("");
    gtk_label_set_markup( GTK_LABEL(widgy), "<span weight='bold'>\nLocal Microphone State</span>" );
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, FALSE, 0 );
/*     gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 ); */

    hbox = gtk_hbox_new ( TRUE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    thing = gtk_hbox_new ( TRUE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    mic_on_btn = gtk_toggle_button_new_with_label("On");
    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mic_on_btn), TRUE );
    
    g_signal_connect( G_OBJECT(mic_on_btn), "toggled",
                      G_CALLBACK (cb_mic_btn), NULL );
    gtk_box_pack_start( GTK_BOX(thing), mic_on_btn, TRUE, TRUE, 0 );
    
    mic_off_btn = gtk_toggle_button_new_with_label("Off");
    g_signal_connect( G_OBJECT(mic_off_btn), "toggled",
                      G_CALLBACK(cb_mic_btn), NULL );
    
    gtk_box_pack_start( GTK_BOX(thing), mic_off_btn, TRUE, TRUE, 0 );
    gtk_box_pack_start( GTK_BOX(hbox), thing, TRUE, TRUE, PADG );
    gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0 );




    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    widgy = gtk_label_new("");
    gtk_label_set_markup( GTK_LABEL(widgy), "<span weight='bold'>\nLocal Speaker Volume</span>" );
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, FALSE, 0 );
    gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 );


    hbox = gtk_hbox_new ( TRUE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    thing = gtk_hbox_new ( TRUE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(thing), BRDR );
    
    vol_dn = gtk_button_new_with_label("Down");
    vol_up = gtk_button_new_with_label("Up");

    gtk_box_pack_start( GTK_BOX(thing), vol_dn, TRUE, TRUE, 0 );
    gtk_box_pack_start( GTK_BOX(thing), vol_up, TRUE, TRUE, 0 );

    g_signal_connect( G_OBJECT(vol_dn), "clicked",
                      G_CALLBACK (cb_vol), NULL );
    g_signal_connect( G_OBJECT(vol_up), "clicked",
                      G_CALLBACK (cb_vol), NULL );
    
    gtk_box_pack_start( GTK_BOX(hbox), thing, TRUE, TRUE, PADG );
    gtk_box_pack_start( GTK_BOX(vbox), hbox,  TRUE, TRUE, 0 );



/*     gtk_box_pack_start( GTK_BOX(outer_vbox), vbox, FALSE, FALSE, 0 ); */

    
    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    widgy = gtk_label_new("");
    gtk_label_set_markup( GTK_LABEL(widgy), "<span weight='bold'>\nRemote Sbuf</span>" );
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, FALSE, 0 );
    gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 );


    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    widgy = gtk_label_new("Buffer time (ms):");
    gtk_box_pack_start( GTK_BOX(hbox), widgy, FALSE, TRUE, PADG );

    adj = gtk_adjustment_new( 100.0, 10.0, 1000.0, 1.0, 10.0, 10.0 );
    sbuf_spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 1.0, 0 );
    gtk_box_pack_start( GTK_BOX(hbox), sbuf_spin, TRUE, TRUE, 0 );


    set_btn = gtk_button_new_with_label("Set");
    g_signal_connect( G_OBJECT(set_btn), "clicked",
                      G_CALLBACK (cb_buftime), NULL );
    gtk_box_pack_start( GTK_BOX(hbox), set_btn, TRUE, TRUE, PADG );
    
    gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0 );


/*     thing = gtk_hseparator_new(); */
/*     gtk_box_pack_start( GTK_BOX(vbox), thing, TRUE, TRUE, 0 ); */
    

/*     hbox = gtk_hbox_new ( TRUE, SPCG ); */
/*     gtk_container_set_border_width( GTK_CONTAINER(hbox), 6 ); */

/*     mic_status = gtk_label_new("mic is ON"); */
/*     gtk_box_pack_start( GTK_BOX(hbox), mic_status, FALSE, FALSE, SPCG ); */

/*     connect_status = gtk_label_new("not connected"); */
/*     gtk_box_pack_start( GTK_BOX(hbox), connect_status, FALSE, FALSE, SPCG ); */

/*     gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 ); */


    /* some space */
    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), SPCG );
    gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0 );

    
    hbox = gtk_hbox_new ( FALSE, SPCG );
    gtk_container_set_border_width( GTK_CONTAINER(hbox), BRDR );

    thing = gtk_button_new_from_stock( GTK_STOCK_QUIT );
    g_signal_connect( G_OBJECT(thing), "clicked",
                      G_CALLBACK (cb_quit), (gpointer)main_window );
    gtk_box_pack_end( GTK_BOX(hbox), thing, FALSE, TRUE, 0 );
    
    gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0 );
    
    gtk_container_add( GTK_CONTAINER(main_window), vbox );


    /* at the beginning most everything is unsensitive */
    gtk_widget_set_sensitive( vol_up, FALSE );
    gtk_widget_set_sensitive( vol_dn, FALSE );
    gtk_widget_set_sensitive( mic_on_btn, FALSE );
    gtk_widget_set_sensitive( mic_off_btn, FALSE );
    gtk_widget_set_sensitive( set_btn, FALSE );
    gtk_widget_set_sensitive( sbuf_spin, FALSE );



    
    gtk_widget_show_all(main_window);
}
