/*
 * 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.
 *
 */
/*
 * $Id: mas_func_device.c,v 1.2 2003/06/26 21:19:23 rocko Exp $
 *
 * Copyright (c) 2000, 2001 by Shiman Associates Inc. and Sun
 * Microsystems, 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.
 *
 * Except as contained in this notice, the names of the authors or
 * copyright holders shall not be used in advertising or otherwise to
 * promote the sale, use or other dealings in this Software without
 * prior written authorization from the authors or copyright holders,
 * as applicable.
 *
 * All trademarks and registered trademarks mentioned herein are the
 * property of their respective owners. No right, title or interest in
 * or to any trademark, service mark, logo or trade name of the
 * authors or copyright holders or their licensors is granted.
 *
 */

/* 2 OCT 2002 - rocko - verified reentrant
 * 2 OCT 2002 - rocko - NOT timestamp clean
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include "mas/mas_dpi.h"
#include "profile.h"
#include "funcparam.h"
#include "functions.h"

#define STOP_STATE  0
#define PAUSE_STATE 1
#define PLAY_STATE  2

struct func_state
{
    int32 reaction;
    int32 source;

    int function;
    int next_function;

    int strst;
    
    struct func_params fp; /* see funcparam.h */

    uint32 media_ts;
};

/*************************************************************************
 * ACTIONS
 *************************************************************************/


/* standard actions ****************************************************/
int32 mas_dev_init_instance( int32 , void* );
int32 mas_dev_show_state( int32 device_instance, void* predicate );

/* device specific actions *********************************************/
int32 mas_func_gen( int32 , void* );

int32
mas_dev_init_instance( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    struct func_params*  fp;
    
    /* Allocate state holder and cast it so we can work on it */
    state       = masc_rtalloc(sizeof(struct func_state));
    if ( state == 0 )
	return mas_error(MERR_MEMORY);

    masd_set_state(device_instance, state); /* set device state */
    memset( state, 0, sizeof (struct func_state) );
    
    fp = &state->fp; /* for convenience */
    
    fp->A            = 1024;
    fp->freq         = 440;
    fp->nextfreq     = fp->freq;
    fp->rate         = 44100;
    fp->segsize      = MAS_FUNC_SEGLEN/4;
    fp->inside       = 2.0 * M_PI * fp->freq / (float)fp->rate;
    fp->samples_in_period = (int)(fp->rate/fp->freq);
    fp->samplenum = 0;
    state->function = SINE_WAVE;
    state->next_function = SINE_WAVE;
    
    masd_get_port_by_name( device_instance, "func_source", &state->source );
    masd_get_port_by_name( device_instance, "reaction", &state->reaction );

    return 0;
}

int32
mas_dev_configure_port( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    struct func_params*  fp;
    struct mas_data_characteristic* dc;
    int32                 sr;
    int32 err;

    masd_get_state(device_instance, (void**)&state);
    err = masd_get_data_characteristic( *(int32*)predicate, &dc );
    
    fp = &state->fp; /* for convenience */
    
    sr = masc_get_index_of_key(dc, "sampling rate");
    
    if ( *(int32*)predicate != state->source )
        return mas_error( MERR_NOTDEF );
    fp->rate = atoi( dc->values[sr] );
    fp->inside       = 2.0 * M_PI * fp->freq / (float)fp->rate;
    fp->samples_in_period = (int)(fp->rate/fp->freq);
    fp->samplenum = 0;
    fp->period_us = (int)( 1000000 * (float)fp->segsize/(float)fp->rate );
    
    return 0;
}

int32
mas_dev_disconnect_port( int32 device_instance, void* predicate )
{
    struct func_state*   state;
    
    masd_get_state(device_instance, (void**)&state);

    state->strst = STOP_STATE;
    
    return 0;
}

int32
mas_dev_exit_instance( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    
    MASD_GET_STATE(device_instance, state);
    masc_rtfree( state );
    
    return 0;
}

int32
mas_dev_terminate( int32 device_instance, void* predicate )
{
    return 0;
}

int32
mas_dev_show_state( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    
    masd_get_state(device_instance, (void**)&state);
    
    masc_log_message(0, "func device %d -----------------", device_instance );
    masc_log_message(0, "    amplitude = %f",  state->fp.A );
    masc_log_message(0, "    frequency = %f", state->fp.freq );
    masc_log_message(0, "sampling rate = %d", state->fp.rate );
    
    return 0;
}

/***************************************************************************
 * mas_func_gen
 *
 *  predicate: unused
 *
 * returns: error
 *
 ***************************************************************************/
int32
mas_func_gen( int32 device_instance, void* predicate )
{
    struct func_state*   state;
    struct mas_data*     data;
    
    masd_get_state(device_instance, (void**)&state);

    if ( state->strst != PLAY_STATE )
    {
        masd_reaction_queue_action_simple( state->reaction, MAS_SCH_INSTANCE, "mas_sch_strike_event", NULL, 0 );
        return 0;
    }
    
    switch (state->function)
    {
    case SINE_WAVE:
        data = gen_sine_wave( &state->fp, MAS_FUNC_SEGLEN );
        break;
    case SQUARE_WAVE:
        data = gen_square_wave( &state->fp, MAS_FUNC_SEGLEN );
        break;
    case TRIANGLE_WAVE:
        data = gen_triangle_wave( &state->fp, MAS_FUNC_SEGLEN );
        break;
    case SAWTOOTH_WAVE:
        data = gen_sawtooth_wave( &state->fp, MAS_FUNC_SEGLEN );
        break;
    case WHITE_NOISE:
        data = gen_white_noise( &state->fp, MAS_FUNC_SEGLEN );
        break;
    case PINK_NOISE:
        data = gen_pink_noise( &state->fp, MAS_FUNC_SEGLEN );
        break;
    default: break;
    }
    
    if ( data == 0 ) return mas_error(MERR_MEMORY);

    /* do we need to switch functions? */
    if (state->next_function != state->function)
    {
        /* soft out, then switch */
        cutout( &state->fp, data );
        state->function = state->next_function;
        state->fp.recompute = TRUE;
    }

    if( state->media_ts == 0 )
    {
        data->header.mark = 1;
    }
    data->header.media_timestamp = state->media_ts;

    state->media_ts += data->length / 4;
    
    masd_post_data( state->source, data );

    return 0;
}

int32
mas_source_play( int32 device_instance, void* predicate )
{
    struct func_state* state;
    int32 err;
    
    masd_get_state(device_instance, (void**)&state);

    state->strst = PLAY_STATE;

    /* schedule periodic action */
    err = masd_reaction_queue_action( state->reaction, device_instance,
                                      "mas_func_gen", 0, 0, 0, 0, 1,
                                      MAS_PRIORITY_ASAP, state->fp.period_us,
                                      0, 0 );
    if (err < 0) return err;
    
    return 0;

}

int32
mas_source_stop( int32 device_instance, void* predicate )
{
    struct func_state* state;
    
    masd_get_state(device_instance, (void**)&state);

    state->strst = STOP_STATE; /* this'll cause the gen action
                                * to bail */
    return 0;
}

int32
mas_get( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    int32 err;
    int32 retport;
    char* key;
    struct mas_package arg;
    struct mas_package r_package;
    /* list of nuggets.  preserve the terminator */
    static char* nuggets[] = 
        { "list", "frequency", "amplitude", "function", "functions", "" };
    int i, n=0;
    
    masd_get_state(device_instance, (void**)&state);

    /* Use the standard get_nugget wrapper. */
    err = masd_get_pre( predicate, &retport, &key, &arg );
    if ( err < 0 ) return err;

    /* construct our response */
    masc_setup_package( &r_package, NULL, 0, MASC_PACKAGE_NOFREE );
    
    /* count the defined nuggets */
    while ( *nuggets[n] != 0 ) n++;

    i = masc_get_string_index(key, nuggets, n);

    switch(i)
    {
    case 0: /*list*/
        masc_push_strings( &r_package, nuggets, n );
        break;
    case 1: /*frequency*/
        masc_pushk_uint32( &r_package, "frequency", state->fp.freq );
        break;
    case 2: /*amplitude*/
        masc_pushk_float( &r_package, "amplitude", state->fp.A );
        break;
    case 3: /*function*/
        masc_pushk_string( &r_package, "function", funcstring[state->function] );
        break;
    case 4: /*functions*/
        while (*funcstring[n] != 0 ) n++;
        masc_push_strings( &r_package, funcstring, n );
        break;
    default:
        break;
    }

    masc_finalize_package( &r_package );
    
    /* post the response where it belongs and free the data structures
     * we abused */
    err = masd_get_post( state->reaction, retport, key, &arg, &r_package );

    return err;
}


int32
mas_set( int32 device_instance, void* predicate )
{
    struct func_state*  state;
    int32 err;
    char* key;
    struct mas_package arg;
    /* list of nuggets.  preserve the terminator */
    static char* nuggets[] = 
        { "frequency", "amplitude", "function", "" };
    int i, n=0;
    char* function;
    uint32 freq;
    
    masd_get_state(device_instance, (void**)&state);

    /* Use the standard get_nugget wrapper. */
    err = masd_set_pre( predicate, &key, &arg );
    if ( err < 0 ) return err;

    /* count the defined nuggets */
    while ( *nuggets[n] != 0 ) n++;

    i = masc_get_string_index(key, nuggets, n);

    switch(i)
    {
    case 0: /*frequency*/
        masc_pullk_uint32( &arg, "frequency", &freq );
        state->fp.nextfreq = (float) freq;
        state->fp.recompute = TRUE;
        break;
    case 1: /*amplitude*/
        masc_pullk_float( &arg, "amplitude", &state->fp.A );
        state->fp.recompute = TRUE;
        break;
    case 2: /*function*/
        masc_pullk_string( &arg, "function", &function, FALSE );

        /* locate string in list of functions */
        while (*funcstring[n] != 0 ) n++;
        state->next_function = masc_get_string_index( function, funcstring, n );
        
        
        break;
    default:
        break;
    }

    /* cleanup after our mess */
    err = masd_set_post( key, &arg );

    return err;
}
