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

#define SEGLEN 4608
#define BUFTIME_MS 52
#define HIGH_WATER_MS 500
#define SLEEP_MS 40

int main(int argc, char* argv[])
{
    int32 err;
    struct mas_data_characteristic* dc;
    mas_device_t mix, sbuf, endian;
    mas_channel_t dchnl;
    mas_port_t rsrc, rsnk, end_snk;
    struct mas_data stack_data;
    struct mas_data* data = &stack_data;
    struct mas_package package;
    int quota = SLEEP_MS * 44.1 * 4;
    uint32 inbuf_ms;
    uint32 seq = 0;
    uint32 ts = 0;
    
    masc_log_verbosity( MAS_VERBLVL_DEBUG );

    masc_log_message( 0, "MAS Sink" );
    masc_log_message( 0, "Takes audio from standard input and plays it using MAS.");
    masc_log_message( 0, "" );
    
    err = mas_init();
    if (err < 0)
    {
	printf("\nconnection with server failed.\n");
	exit(1);
    }

    err = mas_asm_get_device_by_name( "mix", &mix );
    if ( err < 0 ) masc_logerror( err, "get mix" );

    err = mas_make_data_channel( "massink", &dchnl, &rsrc, &rsnk );
    if ( err < 0 ) masc_logerror( err, "couldn't create data channel" );

    err = mas_asm_instantiate_device( "sbuf", 0, 0, &sbuf );
    if ( err < 0 ) masc_logerror( err, "couldn't instantiate sbuf" );
    
    err = mas_asm_instantiate_device( "endian", 0, 0, &endian );
    if ( err < 0 ) masc_logerror( err, "couldn't instantiate endian" );
     
    err = mas_asm_get_port_by_name( endian, "sink", &end_snk );
    if ( err < 0 ) masc_logerror( err, "couldn't get endian sink" );

    dc = masc_make_audio_basic_dc( MAS_LINEAR_FMT, 44100, 16, 2, MAS_LITTLE_ENDIAN_FMT );
    mas_assert( dc != 0, "Memory allocation error" );
    
    err = mas_asm_connect_source_sink( rsrc, end_snk, dc );
    if ( err < 0 ) masc_logerror( err, "Couldn't connect data channel output to endian input." );
    masc_strike_dc( dc );
    masc_rtfree( dc );

    dc = masc_make_audio_basic_dc( MAS_LINEAR_FMT, 44100, 16, 2, MAS_HOST_ENDIAN_FMT );
    mas_assert( dc != 0, "Memory allocation error" );
    
    err = mas_asm_connect_devices_dc( endian, sbuf, "source", "sink", dc );
    if ( err < 0 ) masc_logerror( err, "Couldn't connect endian output to sbuf input." );
    masc_strike_dc( dc );
    masc_rtfree( dc );
    
    err = mas_asm_connect_devices( sbuf, mix, "source", "default_mix_sink" );
    if ( err < 0 ) masc_logerror( err, "Couldn't connect sbuf to mixer." );

    /* set buffer time */
    
    masc_setup_package( &package, NULL, 0, 0 );
    masc_pushk_uint32( &package, "buftime_ms", BUFTIME_MS );
    masc_finalize_package( &package );
    mas_set( sbuf, "buftime_ms", &package );
    masc_strike_package( &package );

    masc_setup_package( &package, NULL, 0, 0 );
    masc_pushk_int32( &package, "mc_clkid", 9 );
    masc_finalize_package( &package );
    mas_set( sbuf, "mc_clkid", &package );
    masc_strike_package( &package );

    mas_source_play( sbuf );
    
    memset( data, 0, sizeof *data );
    data->segment = masc_rtalloc( SEGLEN );
    data->length = SEGLEN;
    data->allocated_length = SEGLEN;
    
    while ( 1 )
    {
        data->header.sequence = seq++;
        data->header.media_timestamp = ts;
        ts += SEGLEN / 4;
        
        if ( fread( data->segment, data->length, 1, stdin ) != 1 )
        {
            if ( feof( stdin ) )
                masc_log_message(0, "Done.");
            else
            {
                masc_log_message(0, "Read error.");
            }
            
            exit(0);
        }
        

        err = mas_send( dchnl, data );
        if ( err < 0 )
        {
            masc_logerror( err, "Couldn't send data to MAS." );
            masc_log_message( MAS_VERBLVL_ERROR, "Exiting.");
            exit(1);
        }

        quota -= data->length;
        if ( quota <= 0 )
        {
            err = mas_get( sbuf, "inbuf_ms", 0 , &package );
            if ( err < 0 )
                masc_logerror( err, "Error getting size of buffer.");

            masc_pull_uint32( &package, &inbuf_ms );
            masc_strike_package( &package );

            /* buffer is full... sleep for a while. */
            if ( inbuf_ms >= HIGH_WATER_MS )
                masc_realsleep( SLEEP_MS * 1000000 );

            /* check buffer again in SLEEP_MS */
            quota = SLEEP_MS * 44.1 * 4;
        }
        
    }
    
    sleep(1);
    
    exit(0);
}
