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

/***************************************************************************
 * mas_reaction_queue_action
 *
 * arguments:
 *  1. reaction_portnum: Portnumber of the local reaction port
 *  2. target_device_instance: the device that the action is specified
 *     relative to.
 *  3. action_name: the name of the action, assumed const char* and
 *     copied to dynamic buffer.
 *  4. predicate: this is not byte-copied, just the pointer is passed.
 *  5. event activation time, seconds portion
 *  6. event activation time, fractional portion
 *  7. boolean, if time_is_rel is true, time is relative to present.
 *     Else, time is absolute.
 *  8. priority level indicator
 *  9. periodic event timer in microseconds.  If this is nonzero,
 *     event will occur at "secs.frac" and then once every "periodic" us.
 *
 *  Constructs a mas_event struct to place in the reaction port's queue.
 *
 * returns: error
 *
 ***************************************************************************/
int32
masd_reaction_queue_action(int32 reaction_portnum, 
			   int32 target_device_instance, 
			   const char* action_name, void* predicate,
			   int32 predicate_len,
			   uint32 secs, uint32 frac, int time_is_rel,
			   int priority, uint32 period, 
			   int32 num_port_dependencies, 
			   int32* port_dependencies)
{
    struct mas_event* event;
    struct mas_data*  data;
    int32             err;

    masc_entering_log_level( "masd_reaction_queue_action" );
    masc_log_message( MAS_VERBLVL_DEBUG+2, "Queuing action\"%s\" device %d, portnum %d.\n", action_name, target_device_instance, reaction_portnum);
    masc_exiting_log_level();
    
    /* for now, don't support arbitrary times, only relative times */
    if ( ( secs != 0 || frac != 0 ) && !time_is_rel )
        return mas_error(MERR_NOSUPP);
    
    /* allocate the event struct */
    event = MAS_NEW( event );
    err = masc_setup_event( event );
    if (err < 0) return mas_error(MERR_MEMORY);

    /* copy stuff to the event struct */
    event->device_instance = target_device_instance;

    /* bytecopy the action name string */
    event->action_name = masc_rtalloc(strlen(action_name) + 1);
    strcpy(event->action_name, action_name);
    
    /* don't bytecopy the predicate */
    event->predicate = predicate;
    event->predicate_length = predicate_len;
    
    /* type undefined */
    event->type = 0;
    
    /* default to the microsecond system clock */
    event->period = period;
    event->clkid = MAS_MC_SYSCLK_US;
    
    event->priority = priority;

    /******** HACK: requires time_is_rel */
    if ( (secs != 0 || frac != 0)  && time_is_rel && !period)
    {
        struct mas_ntpval ntp;
        double reltime;
        
        event->clkid = MAS_MC_SYSCLK_US; /* to make sure */
        masc_get_short_usec_ts( &event->act_time );
        ntp.secs = secs;
        ntp.frac = frac;
        masc_ntp_to_double( &ntp, &reltime );
        event->act_time += (uint32)(reltime * 1.0E6);
    }
    
    /* dependencies: don't bytecopy the dependency array */
    event->num_port_dependencies = num_port_dependencies;
    event->port_dependencies = port_dependencies;
    
    /* stuff the data segment */
    data = MAS_NEW( data );
    data->segment = (void*)event;
    
    /* post data to reaction port */
    if ( ( err = masd_post_data(reaction_portnum, data) ) < 0 )
	return err;
    
    return 0;
}
		       
int32
masd_reaction_queue_event( int32 reaction_portnum, 
			   struct mas_event* event )
{
    struct mas_data*  data;
    int32             err;

    masc_entering_log_level( "masd_reaction_queue_event" );
    masc_log_message( MAS_VERBLVL_DEBUG+2, "Queuing \"%s\" device %d, portnum %d.\n", event->action_name, event->device_instance, reaction_portnum);
    masc_exiting_log_level();
    
    data = MAS_NEW( data );
    if ( data == NULL ) return mas_error(MERR_MEMORY);
    
    /* stuff the data segment */
    data->segment = (void*)event;
    
    /* post data to reaction port */
    if ( ( err = masd_post_data(reaction_portnum, data) ) < 0 )
	return err;
    
    return 0;
}
		       
int32
masd_reaction_queue_action_simple(int32 reaction_portnum,
				  int32 target_device_instance,
				  char* action_name, void* predicate,
				  int32 predicate_len)
{
    return masd_reaction_queue_action(reaction_portnum,
				      target_device_instance,
				      action_name, predicate, predicate_len,
				      0, 0, 1,
				      MAS_PRIORITY_ROUNDTUIT, 0, 0, 0);
}

int32
masd_reaction_queue_action_simple_dep(int32 reaction_portnum,
				      int32 target_device_instance,
				      char* action_name,
				      void* predicate, int32 predicate_len,
				      int32 num_port_dependencies,
				      int32* port_dependencies )
{
    return masd_reaction_queue_action(reaction_portnum,
				      target_device_instance,
				      action_name, predicate, 
				      predicate_len,
				      0, 0, 1,
				      MAS_PRIORITY_ROUNDTUIT, 0, 
				      num_port_dependencies, 
				      port_dependencies);
}

int32
masd_reaction_queue_response(int32 reaction_portnum, 
			     void* response, int32 response_len)
{
    return masd_reaction_queue_action( reaction_portnum,
				       MAS_SCH_INSTANCE,
				       "mas_sch_response", response,
				       response_len, 0, 0, 1,
				       MAS_PRIORITY_ROUNDTUIT, 0, 0,
				       0);
}

/* testing -- hopefully temporary */
int32
masd_reaction_queue_periodic(int32 reaction_portnum, int32 target_device_instance, const char* action_name, void* predicate, int32 predicate_len, int priority, uint32 period, int32 clkid )
{
    struct mas_event* event;
    struct mas_data*  data;
    int32             err;

    masc_entering_log_level( "masd_reaction_queue_periodic" );
    masc_log_message( MAS_VERBLVL_DEBUG+2, "Queuing periodic action\"%s\" device %d, portnum %d.\n", action_name, target_device_instance, reaction_portnum);
    masc_exiting_log_level();

    data = MAS_NEW( data );
    if ( data == NULL ) return mas_error(MERR_MEMORY);
    
    /* allocate the event struct */
    event = MAS_NEW( event );
    err = masc_setup_event( event );
    if (err < 0) return mas_error(MERR_MEMORY);

    /* copy stuff to the event struct */
    event->device_instance = target_device_instance;

    /* bytecopy the action name string */
    event->action_name = masc_rtalloc(strlen(action_name) + 1);
    strcpy(event->action_name, action_name);
    
    /* don't bytecopy the predicate */
    event->predicate = predicate;
    event->predicate_length = predicate_len;
    
    /* type undefined */
    event->type = 0;
    
    /* default to the microsecond system clock */
    event->period = period;
    event->clkid = clkid;
    
    event->priority = priority;

    /* stuff the data segment */
    data->segment = (void*)event;
    
    /* post data to reaction port */
    if ( ( err = masd_post_data(reaction_portnum, data) ) < 0 )
	return err;

    return 0;
}
