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

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

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#ifdef _POSIX_SOURCE
#include <errno.h>
#endif
#include "mas/mas_dpi.h"
#include "profile.h"

char* genre[] = {"Blues",
                 "Classic Rock",
                 "Country",
                 "Dance",
                 "Disco",
                 "Funk",
                 "Grunge",
                 "Hip-Hop",
                 "Jazz",
                 "Metal",
                 "New Age",
                 "Oldies",
                 "Other",
                 "Pop",
                 "R&B",
                 "Rap",
                 "Reggae",
                 "Rock",
                 "Techno",
                 "Industrial",
                 "Alternative",
                 "Ska",
                 "Death Metal",
                 "Pranks",
                 "Soundtrack",
                 "Euro-Techno",
                 "Ambient",
                 "Trip-Hop",
                 "Vocal",
                 "Jazz+Funk",
                 "Fusion",
                 "Trance",
                 "Classical",
                 "Instrumental",
                 "Acid",
                 "House",
                 "Game",
                 "Sound Clip",
                 "Gospel",
                 "Noise",
                 "AlternRock",
                 "Bass",
                 "Soul",
                 "Punk",
                 "Space",
                 "Meditative",
                 "Instrumental Pop",
                 "Instrumental Rock",
                 "Ethnic",
                 "Gothic",
                 "Darkwave",
                 "Techno-Industrial",
                 "Electronic",
                 "Pop-Folk",
                 "Eurodance",
                 "Dream",
                 "Southern Rock",
                 "Comedy",
                 "Cult",
                 "Gangsta",
                 "Top 40",
                 "Christian Rap",
                 "Pop/Funk",
                 "Jungle",
                 "Native American",
                 "Cabaret",
                 "New Wave",
                 "Psychadelic",
                 "Rave",
                 "Showtunes",
                 "Trailer",
                 "Lo-Fi",
                 "Tribal",
                 "Acid Punk",
                 "Acid Jazz",
                 "Polka",
                 "Retro",
                 "Musical",
                 "Rock & Roll",
                 "Hard Rock",
                 "Folk",
                 "Folk-Rock",
                 "National Folk",
                 "Swing",
                 "Fast Fusion",
                 "Bebob",
                 "Latin",
                 "Revival",
                 "Celtic",
                 "Bluegrass",
                 "Avantgarde",
                 "Gothic Rock",
                 "Progressive Rock",
                 "Psychedelic Rock",
                 "Symphonic Rock",
                 "Slow Rock",
                 "Big Band",
                 "Chorus",
                 "Easy Listening",
                 "Acoustic",
                 "Humour",
                 "Speech",
                 "Chanson",
                 "Opera",
                 "Chamber Music",
                 "Sonata",
                 "Symphony",
                 "Booty Bass",
                 "Primus",
                 "Porn Groove",
                 "Satire",
                 "Slow Jam",
                 "Club",
                 "Tango",
                 "Samba",
                 "Folklore",
                 "Ballad",
                 "Power Ballad",
                 "Rhythmic Soul",
                 "Freestyle",
                 "Duet",
                 "Punk Rock",
                 "Drum Solo",
                 "A capella",
                 "Euro-House",
                 "Dance Hall",
                 ""};

struct taginfo
{
    char title[31];
    char artist[31];
    char album[31];
    char year[5];
    char comment[31];
    char genre[31];
    uint8 track;
};

struct tag_state
{
    int32 reaction;
    uint8 genres;
};

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


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

/* device specific actions *********************************************/

static int32 get_tag( struct tag_state* state, char* filename, struct taginfo* tag );

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

    masd_set_state(device_instance, state); /* set device state */
    
    masd_get_port_by_name( device_instance, "reaction", &state->reaction );

    state->genres = 0;

    /* count the genres */
    while ( *genre[state->genres] != 0 )
        state->genres++;
        
    return 0;
}



int32
mas_dev_show_state( int32 device_instance, void* predicate )
{
    struct tag_state*  state;
    
    masd_get_state(device_instance, (void**)&state);
    
    masc_log_message(0, "tag device %d -----------------", device_instance );

    return 0;
}


int32
mas_dev_exit_instance( int32 device_instance, void* predicate )
{
    struct tag_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_get( int32 device_instance, void* predicate )
{
    struct tag_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", "tag", "" };
    int i, n=0;
    char* filename = 0;
    struct taginfo tag;
    
    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: /*tag*/
        if ( arg.contents == NULL )
        {
            masc_pushk_int32( &r_package, "err", mas_error(MERR_NULLPTR) );
            break;
        }

        masc_pull_string( &arg, &filename, FALSE );
        err = get_tag( state, filename, &tag );

        if ( err < 0 )
        {
            masc_pushk_int32( &r_package, "error", err );
        }
        else
        {
            masc_pushk_string( &r_package, "title", tag.title );
            masc_pushk_string( &r_package, "artist", tag.artist );
            masc_pushk_string( &r_package, "album", tag.album );
            masc_pushk_string( &r_package, "year", tag.year );
            masc_pushk_string( &r_package, "comment", tag.comment );
            masc_pushk_string( &r_package, "genre", tag.genre );
            masc_pushk_uint8( &r_package, "track", tag.track );
        }
        
        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
get_tag( struct tag_state* state, char* filename, struct taginfo* tag )
{
    FILE* fp;
    char magic[4];
    int err;
    uint8 genre_byte = 0;
    
    if ( ! (fp = fopen( filename, "r" ) ) )
        return mas_error( MERR_FILE_CANNOT_OPEN );

    err = fseek( fp, -128, SEEK_END );
    if ( err < 0 )
    {
        fclose( fp );
#ifdef _POSIX_SOURCE
        return mas_error(MERR_IO) | mas_make_serror( errno );
#else
        return mas_error(MERR_IO);
#endif
    }

    err = fread( magic, 3, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_IO );
    }

    magic[3] = 0;
    if ( strcmp( magic, "TAG" ) != 0 )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
    
    memset( tag, 0, sizeof *tag );
    
    err = fread( tag->title, 30, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
    
    err = fread( tag->artist, 30, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
    
    err = fread( tag->album, 30, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
    
    err = fread( tag->year, 4, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
        
    err = fread( tag->comment, 30, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }
    
    err = fread( &genre_byte, 1, 1, fp );
    if ( ! err )
    {
        fclose( fp );
        return mas_error( MERR_INVALID );
    }

    /* is this id3v1.1?  - get CD track number */
    if ( tag->comment[28] == 0 )
        tag->track = tag->comment[29];
    else
        tag->track = 0;

    /* copy the genre string */
    if ( genre_byte < state->genres )
        strncpy( tag->genre, genre[genre_byte], 30 );

    masc_trim_string( tag->title );
    masc_trim_string( tag->artist );
    masc_trim_string( tag->album );
    masc_trim_string( tag->comment );
    
    fclose(fp);
    return 0;
}
