/*
 * 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"

void
usage( void )
{
    fprintf(stderr, "usage: masset [-h|-?] [-d id | -n name] key [arg1type arg1key arg1val] ...\n");
    fprintf(stderr, "options:\n");
    fprintf(stderr, "   -d id    do 'set' on device instance 'id'\n");
    fprintf(stderr, "   -n name  do 'set' on first device named 'name'\n");
    fprintf(stderr, "   key      key to set\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "additional arguments require three parameters each:\n");
    fprintf(stderr, "   argNtype type of argument: \n");
    fprintf(stderr, "             %c, %c    int8,  uint8 (expects integers)\n", MAS_INT8, MAS_UINT8);
    fprintf(stderr, "             %c, %c    int16, uint16\n", MAS_INT16, MAS_UINT16);
    fprintf(stderr, "             %c, %c    int32, uint32\n", MAS_INT32, MAS_UINT32);
    fprintf(stderr, "             %c, %c    float, double\n", MAS_FLOAT, MAS_DOUBLE);
    fprintf(stderr, "             %c       string\n", MAS_STRING);
    fprintf(stderr, "             payloads are not suppported\n");
    fprintf(stderr, "   argNkey  argument key\n");
    fprintf(stderr, "   argNval  argument value\n");
    fprintf(stderr, "\n");  
}

int32
push_next_arg( struct mas_package* arg, int argc, char* argv[], int* optind_p )
{
    char arg_type;
    char* arg_key;
    
    if ( *optind_p >= argc )
    {
        masc_log_message( 0, "Not enough arguments\n" );
        return mas_error(MERR_INVALID);
    }
    
    arg_type = *argv[(*optind_p)++];
    
    if ( *optind_p >= argc )
    {
        masc_log_message( 0, "Not enough arguments for type '%c'\n", arg_type );
        return mas_error(MERR_INVALID);
    }
    arg_key = argv[(*optind_p)++];

    if ( *optind_p >= argc )
    {
        masc_log_message( 0, "Not enough arguments for argument '%s' type '%c'\n", arg_key, arg_type );
        return mas_error(MERR_INVALID);
    }

    switch (arg_type)
    {
    case MAS_INT8: masc_pushk_int8( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_UINT8: masc_pushk_uint8( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_INT16: masc_pushk_int16( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_UINT16: masc_pushk_uint16( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_INT32:  masc_pushk_int32( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_UINT32: masc_pushk_uint32( arg, arg_key, atoi( argv[(*optind_p)++] ) );
        break;
    case MAS_FLOAT:
    {
        float f;
        f = atof(argv[(*optind_p)++]);
        /* sscanf( argv[(*optind_p)++], "%f", &f ); */
        masc_pushk_float( arg, arg_key, f );
    }
    break;
    case MAS_DOUBLE:
    {
        double d;
        d = atof(argv[(*optind_p)++]);
        /* sscanf( argv[(*optind_p)++], "%f", &d ); */
        masc_pushk_double( arg, arg_key, d );
    }
    break;
    case MAS_STRING:  masc_pushk_string( arg, arg_key, argv[(*optind_p)++] );
        break;
    default:
        masc_log_message( 0, "Invalid type '%c'\n", arg_type );
        return mas_error(MERR_INVALID);
    }
    
    return 0;
}

int
main(int argc, char* argv[])
{
    int32 err;
    mas_device_t  device;
    struct mas_package arg;
    struct mas_package nugget;
    char name[256];
    char* key;
    int32 device_instance;
    int has_name = FALSE;
    int has_id = FALSE;
    int parse_err = FALSE;
    char c;
    int done_opt = FALSE;
    
    
    while ( !done_opt )
    {
        if ( ( c = getopt( argc, argv, "h?d:n:" ) ) != EOF )
        {
            switch (c)
            {
                case 'n':
                if ( has_id )
                {
                    fprintf(stderr, "-d and -n options are mutually exclusive\n\n");
                    parse_err++;
                }
                else
                {
                    strncpy( name, optarg, 255 );
                    has_name = TRUE;
                    done_opt = TRUE;
                }
            
                break;
                case 'd':
                if ( has_name )
{
                    fprintf(stderr, "-d and -n options are mutually exclusive\n\n");
                    parse_err++;
                }
                else
                {
                    device_instance = atoi( optarg );
                    has_id = TRUE;
                    done_opt = TRUE;
                }
                break;
                case 'h':
                parse_err++;
                break;
                case '?':
                parse_err++;
                break;
                default:
                parse_err++;
                break;
            }
            if ( parse_err )
            {
                usage();
                exit(2);
            }
        }
        else done_opt = TRUE;
    }
    
    /* we need a key, at least.  */
    if ( argc < 2 )
    {
        usage();
        exit(2);
    }

    key = argv[optind++];

    memset( &arg, 0, sizeof arg );

    /* retrieve arguments for the key */
    if (optind < argc)
    {
        masc_setup_package( &arg, NULL, 0, 0 );
        while ( optind < argc )
        {
            err = push_next_arg( &arg, argc, argv, &optind );
            if ( err < 0 )
            {
                masc_log_message( 0, "invalid argument");
                exit(1);
            }
        }
        masc_finalize_package( &arg );
    }

    masc_log_verbosity( MAS_VERBLVL_INFO );

    /* initiate contact with MAS */
    err = mas_init();
    if (err < 0)
    {
	printf("\nconnection with server failed.\n");
	exit(1);
    }

    /*** retrieve device handle as specified */
    /* default to assembler */
    if ( !has_id && !has_name )
    {
        masc_log_message( MAS_VERBLVL_INFO, "using default assembler device handle" );
        err = mas_get_asm_device( &device );
        if ( err < 0 )
        {
            masc_logerror( err, "can't retrieve assembler device handle");
            exit(1);
        }
    }
    else if ( has_name ) /* choose by name */
    {
        err = mas_asm_get_device_by_name( name, &device );
        if ( err < 0 )
        {
            masc_logerror( err, "can't retrieve device handle for '%s'", name);
            exit(1);
        }
        
    }
    else /* choose by ID number */
    {
        masc_log_message(0, "device id not supported yet");
        exit(1);
    }
    
    /* set the nugget */
    masc_entering_log_level("sending query");
    masc_log_message( 0, "key: %s", key );
    if ( arg.contents != NULL )
    {
        masc_log_message( 0, "arg:");
        masc_debug_package( &arg, 0 );
    }
    else masc_log_message( 0, "no arg");

    err = mas_set( device, key, &arg );
    if ( err < 0 )
    {
        masc_logerror( err, "invalid key '%s'", key);
        exit(1);
    }

    masc_exiting_log_level();

    exit(0);
}
