/*
**            AAC plugin for XMMS 1.2.7
**              by ciberfred
**      ------------------------------------------------
** The version of the plugin match the version of XMMS
** for identifie different version use the date :)
**
**          version 1.2.7 (23 august 2002)
**
**
**         need faad2 package from http://www.audiocoding.com
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <glib.h>
#include <gtk/gtk.h>

#include "faad.h"
#include "xmms/plugin.h"
#include "xmms/util.h"
#include "xmms/configfile.h"
#include "xmms/titlestring.h"

#define AAC_DESCRIPTION "MPEG2/4 AAC player - 1.2.7"
#define AAC_VERSION "AAC player - 23 Agust 2002"
#define AAC_ABOUT   "Writen from scratch by ciberfred from France"
#define BUFFER_SIZE FAAD_MIN_STREAMSIZE*64

static void aac_init(void);
static void aac_play(char*);
static void aac_stop(void);
static void aac_pause(short);
static int  aac_getTime(void);
static void aac_seek(int);
static void aac_cleanup(void);
static void aac_about(void);
static void *aac_decode(void*);

static void aac_getSongInfo(char*);
static int  aac_isFile(char*);

extern void readID3tag(char*);
extern GtkWidget *createDialogInfo(void);
static GtkWidget *infoBoxWindow = NULL;
extern char *title, *artist, *album, *track, *genre;
extern void clearWindowDatas(void);
/******************************************************************************/
/*
** struct need by xmms for Library Interface
*/

InputPlugin aac_ip =
{
    0,      // handle
    0,      // filename
    AAC_DESCRIPTION,// description
    aac_init,   // init_func
    aac_about,  // aboutbox
    0,      // configuration
    aac_isFile, // ???
    0,      // scan dir
    aac_play,   // when play button
    aac_stop,   // when stop
    aac_pause,  // when pause
    aac_seek,   // when seek
    0,      // set equalizer
    aac_getTime,    // ???
    0,      // get volume
    0,      // set volume
    aac_cleanup,    // the cleanup function :)
    0,      // obsolete (???)
    0,      // send visualisation data
    0,      // set player window info
    0,      // set song title text
    0,  // get song title text to show on Playlist
    aac_getSongInfo,// file info box
    0       // pointer to outputPlugin
};
static gboolean     bPlaying = FALSE;
static gboolean bOutputOpen = FALSE;
static pthread_t    decodeThread;
static gint     seek_pos = -1; // the track position
static pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;

/******************************************************************************/

InputPlugin *get_iplugin_info(void)
{
    return (&aac_ip);
}

static void aac_init(void)
{
    memset(&decodeThread, 0, sizeof(pthread_t));
}

static void aac_cleanup(void)
{
}

static void aac_play(char *filename)
{
    printf("play\n");
    bPlaying = TRUE;
    pthread_create(&decodeThread, 0, aac_decode, g_strdup(filename));
    return;
}

static void aac_stop(void)
{
    printf("stop\n");
    if (bPlaying){
        bPlaying = FALSE;
        pthread_join(decodeThread, NULL);
        memset(&decodeThread, 0, sizeof(pthread_t));
        aac_ip.output->close_audio();
        clearWindowDatas();
    }
}

static void aac_pause(short paused)
{
    printf("pause\n");
    if(bOutputOpen){
        aac_ip.output->pause(paused);
    }
}

static int aac_getTime(void)
{
    if (!bPlaying){
        return (-1);
    }
    else{
        return (aac_ip.output->output_time());
    }
}


static void aac_seek(int time)
{
    printf("seek\n");
}

static void aac_getSongInfo(char *filename)
{
    infoBoxWindow = createDialogInfo();
    gtk_widget_show(infoBoxWindow);
}

static void *aac_decode(void *args)
{
    char            *filename = args;
    char            *xmmstitle=NULL;
    FILE            *file = NULL;
    faacDecHandle       decoder = 0;
    unsigned char       *buffer = 0;
    unsigned long       bufferconsumed = 0;
    unsigned long       samplerate = 0;
    char            channels;
    unsigned long       buffervalid = 0;
    TitleInput      *input;
    char            *temp = g_strdup(filename);
    char            *ext  = strrchr(temp, '.');

    printf("decoding...\n");
    pthread_mutex_lock(&mutex);
    clearWindowDatas();
    if ((file = fopen(filename, "rb")) == 0){
        printf("can't find file %s\n", filename);
        pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
    }
    if ((decoder = faacDecOpen()) == NULL){
        printf("Open Decoder Error\n");
        fclose(file);
        pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
    }
    if ((buffer = g_malloc(BUFFER_SIZE)) == NULL)
        printf("error g_malloc\n");
    buffervalid = fread(buffer, 1, BUFFER_SIZE, file);
// check for ID3 tag
        XMMS_NEW_TITLEINPUT(input);
        input->file_name = g_basename(temp);
        input->file_ext = ext ? ext+1 : NULL;
        input->file_path = temp;
    if (!strncmp(buffer, "ID3", 3)){
        int size = 0;

        printf("Song with a ID3Tagv2\n");
        readID3tag(filename);
        if(title)
        input->track_name = g_strdup(title);
        if(artist)
        input->performer = g_strdup(artist);
        if(genre)
        input->genre = g_strdup(genre);
        if(track)
        input->track_number = atoi(track);
        fseek(file, 0, SEEK_SET);
/*
** hum .. horrible hack taken from the winamp plugin to jump
** the tag, is there any id3 function to do this ???? hum... seems not :(
*/
        size = (buffer[6]<<21) | (buffer[7]<<14) | (buffer[8]<<7) | buffer[9];
        size+=10;
        fread(buffer, 1, size, file);
        buffervalid = fread(buffer, 1, BUFFER_SIZE, file);
    }
    xmmstitle = xmms_get_titlestring(xmms_get_gentitle_format(), input);
    if(xmmstitle == NULL)
        xmmstitle = g_strdup(input->file_name);
    g_free(temp);
    g_free(input->performer);
    g_free(input->album_name);
    g_free(input->track_name);
    g_free(input->genre);
    g_free(input);
    bufferconsumed = faacDecInit(decoder, buffer, buffervalid, &samplerate, &channels);
//  printf("song with %d channels at %d Hz\n", channels, samplerate);
    if((bOutputOpen = aac_ip.output->open_audio(FMT_S16_NE, samplerate, channels)) == FALSE){
        printf("Output Error\n");
        g_free(buffer);
        buffer=0;
        faacDecClose(decoder);
        fclose(file);
        aac_ip.output->close_audio();
        pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
    }
    aac_ip.set_info(xmmstitle, -1, -1, samplerate, channels);
    aac_ip.output->flush(0);

    while(bPlaying && buffervalid > 0){
        faacDecFrameInfo    finfo;
        unsigned long       samplesdecoded;
        char            *sample_buffer = NULL;

        if(bufferconsumed > 0){
        memmove(buffer, &buffer[bufferconsumed], buffervalid-bufferconsumed);
            buffervalid -= bufferconsumed;
            buffervalid += fread(&buffer[buffervalid], 1, BUFFER_SIZE-buffervalid, file);
            bufferconsumed = 0;
        }
        sample_buffer = faacDecDecode(decoder, &finfo, buffer, buffervalid);
        if(finfo.error){
            buffervalid = 0;
            printf("FAAD2 Error %s\n", faacDecGetErrorMessage(finfo.error));
            printf("---Use Psystrip.exe on the file to avoid the ADTS error---\n");
        }
        bufferconsumed += finfo.bytesconsumed;
        samplesdecoded = finfo.samples;
        if((samplesdecoded<=0) && !sample_buffer){
            printf("error\n");
        }
        while(bPlaying && aac_ip.output->buffer_free() < (samplesdecoded<<1)){
            xmms_usleep(10000);
        }
        aac_ip.add_vis_pcm(aac_ip.output->written_time(), FMT_S16_LE, channels, samplesdecoded<<1, sample_buffer);
        aac_ip.output->write_audio(sample_buffer, samplesdecoded<<1);
    }
    while(bPlaying && aac_ip.output->buffer_playing()){
        xmms_usleep(10000);
    }
//  aac_ip.output->flush(0);
    aac_ip.output->buffer_free();
    aac_ip.output->close_audio();
    bPlaying = FALSE;
    bOutputOpen = FALSE;
    g_free(buffer);
    faacDecClose(decoder);
    g_free(xmmstitle);
    fclose(file);
    printf("...ended\n");
    seek_pos = -1;
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}

static int aac_isFile(char *filename)
{
    char *extention = strrchr(filename, '.');
    if (extention && !strcasecmp(extention, ".aac")){
        return (1);
    }
    return(0);
}

static void aac_about(void)
{
    GtkWidget *dialog, *button, *label, *label2;

    dialog = gtk_dialog_new();
    gtk_window_set_title(GTK_WINDOW(dialog), "About FAAD2 plugin");
    label = gtk_label_new(AAC_ABOUT);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
    gtk_widget_show(label);
    label2 = gtk_label_new(AAC_VERSION);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label2, TRUE, TRUE, 0);
    gtk_widget_show(label2);
    button = gtk_button_new_with_label("close");
    gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(dialog));
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
    gtk_widget_show(button);
    gtk_widget_show(dialog);
}
