/*
 * 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 <string.h>
#include "mas/mas_api_internal.h"
#include "mas_cdrom_common.h"
#include "mas_cdrom.h"

/* Plays a given track number. */
int mas_cdrom_play_track(mas_device_t device, int32 track)
{
    struct mas_event   event;
    struct mas_package predpack;
    struct mas_package rpkg;
    char buffer[1024];
    int32 err;

    masc_setup_event( &event );
    event.action_name = "mas_cdrom_play_track";
    event.device_instance = device->device_instance;

    /* stuff the predicate package */
    masc_setup_package( &predpack, buffer, sizeof buffer, MASC_PACKAGE_STATIC );
    masc_push_int32( &predpack, track );
    masc_finalize_package( &predpack );
    event.predicate = predpack.contents;
    event.predicate_length = predpack.size;
    
    /* send it to the server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &predpack );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}

/* Starts playing the track at the minute, seconds, frame location */
int mas_cdrom_play_msf(mas_device_t device, int32 minute, int32 second, int32 frame)
{
    struct mas_event   event;
    struct mas_package predpack;
    struct mas_package rpkg;
    char buffer[1024];
    int32 err;

    masc_setup_event( &event );
    event.action_name = "mas_cdrom_play_msf";
    event.device_instance = device->device_instance;

    /* stuff the predicate package */
    masc_setup_package( &predpack, buffer, sizeof buffer, MASC_PACKAGE_STATIC );
    masc_push_int32(&predpack, minute);
    masc_push_int32(&predpack, second);
    masc_push_int32(&predpack, frame);
    masc_finalize_package( &predpack );
    event.predicate = predpack.contents;
    event.predicate_length = predpack.size;
    
    /* send it to the server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &predpack );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}

/* Sets the state of the cd player. */
int mas_cdrom_set_status(mas_device_t device, int cd_status)
{
    struct mas_event   event;
    struct mas_package predpack;
    struct mas_package rpkg;
    char buffer[1024];
    int32 err;

    masc_setup_event( &event );
    event.action_name = "mas_cdrom_set_status";
    event.device_instance = device->device_instance;

    /* stuff the predicate package */
    masc_setup_package( &predpack, buffer, sizeof buffer, MASC_PACKAGE_STATIC );
    masc_push_int32(&predpack, cd_status);
    masc_finalize_package( &predpack );
    event.predicate = predpack.contents;
    event.predicate_length = predpack.size;
    
    /* send it to the server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &predpack );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}

/* Sets the state of the cd player. */
CD_Status *mas_cdrom_get_status(mas_device_t device)
{
    CD_Status		*cd_status;
    struct mas_event	event;
    struct mas_package rpkg;
    int32 ret_val, err;
    
    /* make an event */
    masc_setup_event(&event);
    event.action_name = "mas_cdrom_get_status";
    event.device_instance = device->device_instance;
    
    /* send it to the server */
    mas_send_event( &event, device->control_channel );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) goto failure;
    
    masc_pull_int32(&rpkg, &ret_val);

    /* If ret_val is true, cd data was successfully retrieved */
    if(ret_val)
    {
        /* Allocate space to place the query */
        if((cd_status = (CD_Status*)calloc(1, sizeof(CD_Status))) == NULL)
        {
            fprintf(stderr, "malloc() returned NULL.\n");
            goto failure;
        }

        masc_pull_string(&rpkg, &(cd_status->device_location), TRUE);
        masc_pull_int32(&rpkg, &(cd_status->status));
        masc_pull_int32(&rpkg, &(cd_status->current_track));
        masc_pull_int32(&rpkg, &(cd_status->absolute_msf.minute));
        masc_pull_int32(&rpkg, &(cd_status->absolute_msf.second));
        masc_pull_int32(&rpkg, &(cd_status->absolute_msf.frame));
        masc_pull_int32(&rpkg, &(cd_status->relative_msf.minute));
        masc_pull_int32(&rpkg, &(cd_status->relative_msf.second));
        masc_pull_int32(&rpkg, &(cd_status->relative_msf.frame));
    }
    else
        cd_status = NULL;

 failure:
    masc_strike_package(&rpkg);

    return cd_status;
}

/* This fetches the cd's info from the freedb.org database */
CD_Information *mas_cdrom_get_info(mas_device_t device, char *cddb_server, char *username)
{
    struct mas_event   event;
    struct mas_package predpack;
    struct mas_package rpkg;
    char buffer[1024];
    int			i;
    int			ret_val, err;
    CD_Information		*cd_info = NULL;

    masc_setup_event( &event );
    event.action_name = "mas_cdrom_get_info";
    event.device_instance = device->device_instance;

    /* stuff the predicate package */
    masc_setup_package( &predpack, buffer, sizeof buffer, MASC_PACKAGE_STATIC );

    /* If cddb_server is NULL don't look up the cddb info */
    if(cddb_server)
    {
        masc_push_int32(&predpack, 1);
        masc_push_string(&predpack, cddb_server);
        masc_push_string(&predpack, username);
    }
    else
    {
        masc_push_int32(&predpack, 0);
    }

    masc_finalize_package( &predpack );
    event.predicate = predpack.contents;
    event.predicate_length = predpack.size;
    
    /* send it to the server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &predpack );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) goto failure;

    /* Allocate space to place the query */
    if((cd_info = (CD_Information*)calloc(1, sizeof(CD_Information))) == NULL)
    {
        fprintf(stderr, "malloc() returned NULL.\n");
        goto failure;
    }
	
    masc_pull_int32(&rpkg, &ret_val);

    /* If the return code is >0 then the cddb query was successful */
    if(ret_val)
    {
        /* First pull the track info */
        masc_pull_int32(&rpkg, &(cd_info->number_of_tracks));
        if((cd_info->tracks = (struct track*)calloc(cd_info->number_of_tracks, sizeof(struct track))) == NULL)
        {
            fprintf(stderr, "malloc() returned NULL.\n");
            goto failure;
        }
        for(i=0; i<cd_info->number_of_tracks; i++)
        {
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].start_msf.minute));
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].start_msf.second));
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].start_msf.frame));
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].length_msf.minute));
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].length_msf.second));
            masc_pull_int32(&rpkg, &(cd_info->tracks[i].length_msf.frame));
        }

        /* Now pull the cddb info if it was sent */
        if(ret_val == 2)
        {
            masc_pull_int32(&rpkg, &(cd_info->cddb_id));
            masc_pull_string(&rpkg, &(cd_info->title), TRUE);
            masc_pull_string(&rpkg, &(cd_info->genre), TRUE);
            masc_pull_string(&rpkg, &(cd_info->year), TRUE);
            masc_pull_string(&rpkg, &(cd_info->misc_data), TRUE);
            for(i = 0; i < cd_info->number_of_tracks; i++)
                masc_pull_string(&rpkg, &(cd_info->tracks[i].trackname), TRUE);
        }
        else
        {
            /* Fill in null fields instead */
            cd_info->title = calloc(1, strlen("\0"));
            cd_info->genre = calloc(1, strlen("\0"));
            cd_info->year = calloc(1, strlen("\0"));
            cd_info->misc_data = calloc(1, strlen("\0"));
            for(i = 0; i < cd_info->number_of_tracks; i++)
                cd_info->tracks[i].trackname = calloc(1, strlen("\0"));
        }
    }

    goto success;
 failure:
    if(cd_info) free(cd_info);
    cd_info = NULL;
 success:
    masc_strike_package(&rpkg);
    return cd_info;
}

/* Exits the program */
int mas_cdrom_exit_instance(mas_device_t device)
{
    mas_asm_terminate_device_instance( device, 2 );
    return 1;
}

void destroy_cd_info(CD_Information *cd_info)
{
	int i;


	if(cd_info->genre) free(cd_info->genre);
	if(cd_info->title) free(cd_info->title);
	if(cd_info->year) free(cd_info->year);
	if(cd_info->misc_data) free(cd_info->misc_data);

	for(i = 0; i < cd_info->number_of_tracks; i++)
		free(cd_info->tracks[i].trackname);
	free(cd_info->tracks);
	
	free(cd_info);
}

void destroy_cd_status(CD_Status *cd_status)
{
	free(cd_status->device_location);
	free(cd_status);
}
