#include <glib.h>

#include "mac.h"

#include <mac/APETag.h>
#include <mac/APEInfo.h>
#include <mac/CharacterHelper.h>
#include <mac/MACLib.h>

#include <bmp/util.h>
#include <bmp/titlestring.h>
#include <bmp/playlist.h>


InputPlugin mac_plugin_info = 
{
    NULL,
    NULL,
    NULL,
    mac_init,
    mac_about,
    mac_configure,
    mac_is_our_file,
    NULL,
    mac_play_file,
    mac_stop,
    mac_pause,
    mac_seek,
    NULL,
    mac_get_time,
    NULL,
    NULL,
    mac_cleanup,
    NULL,
    NULL,
    NULL,
    NULL,
    mac_get_song_info,
    mac_file_info_box,
    NULL
};

static PlayerInfo *mac_info;

#ifdef __cplusplus
extern "C"{
#endif

static char *get_file_extname(const char *filename)
{
	char *ext = strrchr(filename, '.');

	if (ext != NULL)
		++ext;

	return ext;
}

static char *get_tag_info(CAPETag *tag, wchar_t *fieldname)
{
    if (!tag)
    {
	return NULL;
    }


    CAPETagField *pTagField = tag->GetTagField(fieldname);

    if (pTagField == NULL)
    {
	return "";
    }

    const char *fieldValue;
    char *value;

    fieldValue = pTagField->GetFieldValue();
    if (tag->GetAPETagVersion() == CURRENT_APE_TAG_VERSION)
    {
	value = GetANSIFromUTF8((unsigned char *)fieldValue);
    }
    else
    {
	value = (char *)g_malloc0(255);
	strcpy(value, fieldValue);
    }

    char *ret = g_strdup(value);
    free(value);
    
    return ret;
}
/*
#undef  XMMS_NEW_TITLEINPUT
#define XMMS_NEW_TITLEINPUT(input) G_STMT_START {       \
        input = (TitleInput *)g_malloc0(sizeof(TitleInput));          \
        input->__size = XMMS_TITLEINPUT_SIZE;           \
        input->__version = XMMS_TITLEINPUT_VERSION;     \
} G_STMT_END
*/

static char *mac_format_title_string(char *name, CAPETag *tag)
{
    gchar *filename = g_strdup(name);

    char *ret = NULL; 
    TitleInput *input = NULL;
    bool hasTag = false;

    if (tag && (tag->GetHasID3Tag() || tag->GetHasAPETag()))
    {
	hasTag = true;

	input = bmp_title_input_new();

	input->performer = get_tag_info(tag, APE_TAG_FIELD_ARTIST);
	input->album_name = get_tag_info(tag, APE_TAG_FIELD_ALBUM);
	input->track_name = get_tag_info(tag, APE_TAG_FIELD_TITLE);
	input->track_number = atoi(get_tag_info(tag, APE_TAG_FIELD_TRACK));
	input->year = atoi(get_tag_info(tag, APE_TAG_FIELD_YEAR));
	input->genre = get_tag_info(tag, APE_TAG_FIELD_GENRE);
	input->comment = get_tag_info(tag, APE_TAG_FIELD_COMMENT);

	input->file_name = g_strdup(g_basename(filename));
	input->file_path = filename;
	input->file_ext = get_file_extname(filename);

	ret = xmms_get_titlestring(xmms_get_gentitle_format(), input);

	g_free(input);
	
    }

    if (!hasTag && !ret)
    {
	ret = (char *)g_strdup(g_basename(filename));
    }
    
    return ret;
}

static int decompress_init(void *pEntry)
{

    PlaylistEntry *Entry = (PlaylistEntry *)pEntry;

    wchar_t *pUTF16 = GetUTF16FromANSI(Entry->filename);
    int nRet;

    mac_info = (PlayerInfo *)g_malloc0(sizeof(PlayerInfo));

    mac_info->playing = false;
    mac_info->eof = false;
    mac_info->seek_to = -1;

    IAPEDecompress *decompress = NULL;

    CAPEInfo *ApeInfo = new CAPEInfo(&nRet, pUTF16);

    if (nRet) return -1;
    mac_info->sample_rate = ApeInfo->GetInfo(APE_INFO_SAMPLE_RATE, 0, 0);
    mac_info->bits_per_sample = ApeInfo->GetInfo(APE_INFO_BITS_PER_SAMPLE, 0, 0);
    mac_info->channels = ApeInfo->GetInfo(APE_INFO_CHANNELS, 0, 0);
	mac_info->length_in_ms = ApeInfo->GetInfo(APE_INFO_LENGTH_MS, 0, 0);
    mac_info->block_align = ApeInfo->GetInfo(APE_INFO_BLOCK_ALIGN, 0, 0);
    mac_info->bitrate = ApeInfo->GetInfo(APE_INFO_AVERAGE_BITRATE, 0, 0);
    double StartBlock = -1.0, FinishBlock = -1.0;
    if (Entry->ext && Entry->ext->seek != -1) {
        int total_blocks = ApeInfo->GetInfo(APE_INFO_TOTAL_BLOCKS, 0, 0);
        StartBlock = total_blocks / mac_info->length_in_ms * Entry->ext->seek;
        if (Entry->length==-1) {  /* the last track length=-1*/
            FinishBlock = total_blocks;
            Entry->length = mac_info->length_in_ms - Entry->ext->seek;
        }
        else
            FinishBlock = total_blocks  / mac_info->length_in_ms * (Entry->ext->seek + Entry->length);
    }
    decompress = CreateIAPEDecompressEx2(ApeInfo, (int)StartBlock, (int)FinishBlock, &nRet);

    
    if (!decompress || nRet != ERROR_SUCCESS)
    {
	return -1;
    }    

    mac_info->pAPEDecompress = decompress;
    mac_info->pInfo = ApeInfo;
    CAPETag *tag = (CAPETag *)decompress->GetInfo(APE_INFO_TAG);
    mac_info->title = mac_format_title_string(Entry->filename, tag);
    mac_info->sample_format = (mac_info->bits_per_sample == 16) ? FMT_S16_LE : FMT_S8;
    mac_info->decoder_thread_id = 0;

    delete [] pUTF16;

    
    return 0;

}

#define SAMPLES_PER_READ 512

static void *decode_loop(void *arg)
{
    char data[9216];

    int blocks_to_read, actrual_read;
    int nRetVal;
    int bytes, actrual_bytes;

    blocks_to_read = SAMPLES_PER_READ;
    bytes = blocks_to_read * mac_info->block_align;

    while (mac_info->playing)
    {
	if (!mac_info->eof)
	{
	    actrual_read = blocks_to_read;
	    actrual_bytes = bytes;
	    nRetVal = mac_info->pAPEDecompress->GetData(data, blocks_to_read, &actrual_read);        

	    if (actrual_read)
	    {
		if (actrual_read < blocks_to_read)
		{
		    actrual_bytes = actrual_read * mac_info->block_align;
		}
		bytes = blocks_to_read * (mac_info->bits_per_sample / 8) * mac_info->channels;

		mac_plugin_info.add_vis_pcm(mac_plugin_info.output->written_time(),
					    mac_info->sample_format, mac_info->channels,
					    actrual_bytes, data);


		while (mac_plugin_info.output->buffer_free() < actrual_bytes 
		       && mac_info->playing 
		       && mac_info->seek_to == -1)
		{
		    xmms_usleep(10000);
		}
		if (mac_info->playing && mac_info->seek_to == -1)
		{
//		    printf("actrual bytes = %d\n", actrual_bytes);
		    mac_plugin_info.output->write_audio(data, actrual_bytes);
		}

	    }
	    else
	    {
		mac_info->eof = true;
		mac_plugin_info.output->buffer_free();
		xmms_usleep(10000);
	    }
	    
	}
	else
	{
	    xmms_usleep(10000);
	}

	if (mac_info->seek_to != -1)
	{
	    int to_block = mac_info->seek_to * mac_info->sample_rate;

	    mac_info->pAPEDecompress->Seek(to_block);
	    mac_plugin_info.output->flush(mac_info->seek_to * 1000);
	    mac_info->seek_to = -1;
	}
    }
    pthread_exit(NULL);

}

/***********************************************
 *                                             *
 *             Interface Functions   *
 *                                             *
 ***********************************************/


InputPlugin *get_iplugin_info()
{
    mac_plugin_info.description = g_strdup_printf("Monkey's Audio Codec Player %s", VERSION);
    return &mac_plugin_info;
}


void mac_init()
{
}

void mac_about()
{
    static GtkWidget *aboutbox = NULL;

    if (aboutbox != NULL)
	return;
	
    aboutbox = bmp_info_dialog(
	_("About Monkey's Audio Codec plugin"),
	_("MAC decoding engine by Matthew T. Ashland <email@monkeysaudio.com>\nPlugin by SuperMMX <SuperMMX@163.com>"),
	_("OK"), FALSE, NULL, NULL);

    g_signal_connect(GTK_OBJECT(aboutbox), "destroy",
		     GTK_SIGNAL_FUNC(gtk_widget_destroyed), &aboutbox);
}

void mac_configure()
{
}

int mac_is_our_file(char *filename)
{
    char *ext;

    ext = strrchr(filename, '.');
    if(ext)
    {
	if(!strcasecmp(ext, ".mac") || 
	   !strcasecmp(ext, ".ape") ||
	   !strcasecmp(ext, ".apl"))
	{
	    return true;
	}
    }
    return false;
}

void mac_play_file(char *filename, void *pEntry)
{
    if (!pEntry) return;

    
    PlaylistEntry *curr = (PlaylistEntry *)pEntry;


    if (decompress_init(pEntry) < 0)
    {
	return;
    }
    mac_info->playing = true;


    if(mac_plugin_info.output->open_audio(mac_info->sample_format,
                                          mac_info->sample_rate,
                                          mac_info->channels) == 0)
    {
        return;
    }
    if (curr->ext && curr->ext->seek != -1) {
        mac_plugin_info.set_info(g_strdup(curr->title), curr->length,
                                 mac_info->bitrate * 1000,
                                 mac_info->sample_rate, mac_info->channels);
    }
    else {
        mac_plugin_info.set_info(g_strdup(mac_info->title), mac_info->length_in_ms, 
                                 mac_info->bitrate * 1000,
                                 mac_info->sample_rate, mac_info->channels);
    }

    pthread_create(&mac_info->decoder_thread_id, NULL, decode_loop, NULL);
}

void mac_stop()
{
    if(mac_info->playing) 
    {
	mac_info->playing = false;
	pthread_join(mac_info->decoder_thread_id, NULL);
	mac_plugin_info.output->close_audio();
	
	if (mac_info->pAPEDecompress)
	{
	    delete mac_info->pAPEDecompress;
	    mac_info->pAPEDecompress = NULL;
    }
    /* 
     * if (mac_info->pInfo)
     * {
     *     delete mac_info->pInfo;
     *     mac_info->pInfo = NULL;
     * }
     */
    
	g_free(mac_info);
	mac_info = NULL;
    }

}

void mac_pause(short paused)
{
    mac_plugin_info.output->pause(paused);
}

void mac_seek(int time)
{

    mac_info->seek_to = time;
    mac_info->eof = false;

    while (mac_info->seek_to != -1)
    {
	xmms_usleep(10000);
    }

}

int mac_get_time()
{
    if (!mac_info)
    {
	return -1;
    }
    if (!mac_info->playing || (mac_info->eof && !mac_plugin_info.output->buffer_playing()))
    {
	return -1;
    }
    else
    {
	return mac_plugin_info.output->output_time();
    }
}

void mac_cleanup()
{    
}

void mac_get_song_info(char *filename, char **title, int *length)
{
    int nRetVal = 0;

    if (filename == NULL)
	return;

    wchar_t *pUTF16Name = GetUTF16FromANSI(filename);
    IAPEDecompress *pDec = CreateIAPEDecompress(pUTF16Name, &nRetVal);

    if (nRetVal != ERROR_SUCCESS)
    {
	if (title)
	{
	    static const char *errtitle = "Invalid MAC File: ";
	    *title = (char *)g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1);
	    sprintf(*title, "%s\"%s\"", errtitle, filename);
	}
	if (length)
	{
	    *length = -1;
	}
	return;
    }

    if (title)
    {
	CAPETag *apeTag = (CAPETag *)pDec->GetInfo(APE_INFO_TAG);
	*title = mac_format_title_string(filename, apeTag);
    }
    if (length)
    {
	*length = pDec->GetInfo(APE_DECOMPRESS_LENGTH_MS);
    }

    delete pDec;
}

#ifdef __cplusplus
}
#endif

