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

#define _NONE_OR_STR(x) (x)?(x):"(none)"

/***********************************************************************
 * DEBUGGING HELPERS
 ***********************************************************************/

int32
masi_print_device_profile( struct mas_device_profile* profile, int flags )
{
    int i = 0;
    char tstr[256];
    
    masc_log_message( 0, "             name: %s", _NONE_OR_STR(profile->name));
    masc_log_message( 0, "          purpose: %s", _NONE_OR_STR(profile->purpose));
    masc_log_message( 0, "         filename: %s", _NONE_OR_STR(profile->library_filename));
    masc_log_message( 0, "      description: %s", _NONE_OR_STR(profile->description));
    masc_log_message( 0, "       build_date: %s", _NONE_OR_STR(profile->build_date));
    masc_log_message( 0, "       build_host: %s", _NONE_OR_STR(profile->build_host));
    masc_log_message( 0, "          version: %u.%u.%u", profile->major_version, profile->minor_version, profile->teeny_version);
    masc_log_message( 0, "        reentrant: %s", profile->reentrant?"yes":"no");
    masc_log_message( 0, "media ts accurate: %s", profile->mts_acc?"yes":"no");
    masc_log_message( 0, "  NTP ts accurate: %s", profile->ntpts_acc?"yes":"no");
    if ( flags & MAS_PRINT_LICENSE )
    {
        masc_log_message( 0, "          license: ");
        masc_log_message( 0, "%s", _NONE_OR_STR(profile->license));
    }
    else
    {
        strncpy( tstr, _NONE_OR_STR(profile->license), 60 );
        if ( profile->license && strlen( profile->license ) > 60 )
        {
            strncpy( &(tstr[46]), "[...truncated]", 14);
            tstr[60] = 0;
        }
        masc_log_message( 0, "          license: %s", tstr);
    }

    masc_log_message( 0, "       actions: ");
    for (i=0; i<profile->actions; i++)
    {
	masc_log_message( 0, "                %s (0x%x)", profile->action_names[i], profile->action[i]);
    }

    if ( profile->clock_names )
    {
        if ( profile->clocks == 0 )
        {
            masc_log_message( 0, "  clocks impl.: (none)");
        }
        else
        {
            masc_log_message( 0, "  clocks impl.: ");
            for (i=0; i<profile->clocks; i++)
            {
                masc_log_message( 0, "                %s", profile->clock_names[i]);
            }
        }
    }
    else
    {
        masc_log_message( 0, "  clocks impl.: (none)");
    }
    
    masc_log_message( 0, "  action stats: ");
    for (i=0; i<profile->actions; i++)
    {
	masc_log_message( 0, "                %s", profile->action_names[i]);
	masi_print_action_statistics( &(profile->action_wallclock_stats[i]) );
        masc_log_message( 0, "" );
    }

    if ( profile->ports )
    {
        masc_log_message( 0, "  static ports: ");
        for (i=0; i<profile->ports; i++)
        {
            if ( profile->port_types[i] == MAS_SINK ) 
                masc_log_message( 0,
                                  "                (\"%s\", MAS_SINK, %s)",
                                  profile->port_names[i],
                                  profile->port_cmatrices[i]);
            if ( profile->port_types[i] == MAS_SOURCE ) 
                masc_log_message( 0,
                                  "                (\"%s\", MAS_SOURCE, %s)",
                                  profile->port_names[i],
                                  profile->port_cmatrices[i]);
            if ( profile->port_types[i] == MAS_REACTION ) 
                masc_log_message( 0,
                                  "                (\"%s\", reaction, %s)",
                                  profile->port_names[i],
                                  profile->port_cmatrices[i]);
        }
    }
    else
    {
        masc_log_message( 0, "  static ports: (none)");
    }

    if ( profile->cmatrices )
    {
        if ( flags & MAS_PRINT_CMATRIX )
        {
            masc_log_message( 0, "char. matrices: ");
            for (i=0; profile->cmatrix_names[i][0] != 0; i++)
            {
                masc_log_message( 0, "                %s (%d columns, %d rows)", profile->cmatrix_names[i], profile->cmatrix[i].cols, profile->cmatrix[i].rows);
            }
        }
        else
        {
            masc_log_message( 0, "char. matrices: [not displayed]");
        }
        masc_log_message( 0, "");
    }
    else
    {
        masc_log_message( 0, "char. matrices: (none)");
    }
    
    return 0;
}

int32
masi_print_characteristic_matrix( struct mas_characteristic_matrix* cmatrix )
{
    int i = 0;
    int j = 0;
    char preprint[2048];
    char tempstr[256];
    
    masc_log_message( 0, "characteristic matrix \"%s\" (%d columns, %d rows)", cmatrix->name, cmatrix[i].cols, cmatrix[i].rows);

    preprint[0] = 0;
    for (j=0; j<cmatrix->cols; j++)
    {
        sprintf(tempstr, "%-15s ", cmatrix->keys[j]);
        strcat( preprint, tempstr );
    }
    masc_log_message( 0, preprint);

    preprint[0] = 0;
    for (j=0; j<cmatrix->cols; j++)
    {
        sprintf(tempstr, "--------------- ");
        strcat( preprint, tempstr );
    }
    masc_log_message( 0, preprint);

    for (i=0; i<cmatrix->rows; i++)
    {
        preprint[0] = 0;
	for (j=0; j<cmatrix->cols; j++)
	{
	    sprintf( tempstr, "%-15s ", cmatrix->matrix[i][j]);
            strcat( preprint, tempstr );
	}
	masc_log_message( 0, preprint);
    }

    masc_log_message( 0, "");

    return 0;
}


int32
masi_print_data_characteristic( struct mas_data_characteristic* dc )
{
    int i = 0;
    /* cleanup */
    
    for (i=0; i<dc->numkeys; i++) 
	masc_log_message( 0, "%-15s ", dc->keys[i]);
    masc_log_message( 0, "");
    for (i=0; i<dc->numkeys; i++) 
	masc_log_message( 0, "%-15s ", dc->values[i]);
    masc_log_message( 0, "");

    return 0;
}

int32
masi_print_action_statistics( struct mas_stats* stat )
{
    masc_stats_recompute_window( stat );

    if ( stat->count == 0 )
    {
        masc_log_message( 0, "                   NO DATA", stat->count);
        return 0;
    }
    
    masc_log_message( 0, "                                  N  mean(us)  sdev(us)   min(us)   max(us)");
    
    masc_log_message( 0, "                   total: % 9lu % 9.f       n/a % 9.f % 9.f", stat->count, stat->mean, stat->min, stat->max);
    masc_log_message( 0, "                windowed: % 9lu % 9.f % 9.f % 9.f % 9.f", stat->N, stat->win_mean, stat->win_std_dev, stat->win_min, stat->win_max);
    
    return 0;
}

int32
masi_print_event( struct mas_event* event )
{
    int i = 0;
#define TSTR_LEN 256
    char ts[TSTR_LEN];
    int  remain = TSTR_LEN - 1;
    
    masc_log_message( 0, "       action name: %s", event->action_name);
    masc_log_message( 0, "                id: %u", event->id );
    masc_log_message( 0, "   device instance: %d", event->device_instance);

    if ( event->type > 0 )
        masc_log_message( 0, "              type: %s", event->type);

    if ( event->period == 1 && event->clkid == MAS_MC_SYSCLK_US )
        masc_log_message( 0, "            period: epsilon (dataflow dependency)");
    else
        masc_log_message( 0, "            period: %u [clock %02d]", event->period, event->clkid);
    masc_log_message( 0, "     last_act_time: %u [clock %02d], %u [sc]", event->last_act_time, event->clkid, event->last_act_time_sc);
    masc_log_message( 0, "     next_act_time: %u [clock %02d], %u [sc]", event->next_act_time, event->clkid, event->next_act_time_sc);
    masc_log_message( 0, "          act_time: %u [clock %02d], %u [sc]", event->act_time, event->clkid, event->act_time_sc);
    
    masc_log_message( 0, "          priority: %d", event->priority);
    
    masc_log_message( 0, "      source event: %d", event->source_event_id);
    masc_log_message( 0, "source device inst: %d", event->source_device_instance);
    masc_log_message( 0, "source device subs: %d", event->source_device_subscript);
    
    if ( event->num_port_dependencies > 0 )
    {
	for (i=0; i<event->num_port_dependencies; i++)
	{
            snprintf( ts, remain, "%d", event->port_dependencies[i]);
            remain = TSTR_LEN - strlen( ts ) - 1;
	    if ( i < event->num_port_dependencies - 1 )
		snprintf( ts, remain, ", ");
            remain = TSTR_LEN - strlen( ts ) - 1;
	}
        masc_log_message( 0, " port dependencies: %s", ts);
	masc_log_message( 0, "");
    }

    if ( event->signal_dep > 0 )
    {
        masc_log_message( 0, "       signal deps: %d", event->signal_dep);
    }
    
    return 0;
}

int32
masi_print_event_queue( struct mas_event* head )
{
    struct mas_event* event = head;
    
    masc_log_message( 0, "** current event queue: ");
    if ( head == 0 )
    {
	masc_log_message( 0, "(empty)");
	return 0;
    }
    if ( head->next == 0 && head->action_name == 0 )
    {
	masc_log_message( 0, "(empty)");
	return 0;
    }

    while ( event )
    {
	masc_log_message( 0, "*---------------------------------------------------------");
	if ( event->action_name != 0)
	    masi_print_event( event );
	event = event->next;
    }
    masc_log_message( 0, "*---------------------------------------------------------");
    
    return 0;
}

int32
masi_print_data( struct mas_data* data )
{
    masc_log_message( 0, "-- data ----------------------------------------------------------------");
    masc_log_message( 0, "     header.ntp_seconds: %u", data->header.ntp_seconds);
    masc_log_message( 0, "    header.ntp_fraction: %u", data->header.ntp_fraction);
    masc_log_message( 0, " header.media_timestamp: %u", data->header.media_timestamp);
    masc_log_message( 0, "        header.sequence: %u", data->header.sequence);
    masc_log_message( 0, "                 length: %u", data->length);
    masc_log_message( 0, "       allocated length: %u", data->allocated_length);
    masi_show_hex(data->segment, data->length);
    masc_log_message( 0, "");

    return 0;
}
int32
masi_print_data_queue( struct mas_data* data )
{
    while (data)
    {
	masi_print_data( data );
	data = data->next;
    }

    return 0;
}

void
masi_show_hex(char *buffer, int len)
{
    int i, j, k;

    for (i=0; i<len; i+=16)
    {
	printf( "%04X: ", i);
	for (j=i; (j<(i+16))&&(j<len); j++)
	{
	    printf( "%02X ", (unsigned char) *(buffer++));
	}
	buffer-=j-i;
	printf(  "\n");
	for (k=j; k<(i+16); k++) printf( "   ");
	for (j=i; (j<(i+16))&&(j<len); j++)
	{
	    if ((*buffer)>31)
		printf( "%c", *(buffer++));
	    else
	    {
		printf( ".");
		buffer++;
	    }
	}
	printf( "\n");
    }
}
