#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <unistd.h>
#include "../include/string.h"
#include "../include/fio.h"
#include "../include/prochandle.h"
#include "audiocd.h"

AudioCDContext *AudioCDInit(void);
void AudioCDShutdown(AudioCDContext *ctx);

AudioCDTrack **AudioCDListTracks(AudioCDContext *ctx, int *total);
void AudioCDDeleteTracksList(AudioCDTrack **track, int total);

int AudioCDLastTrack(AudioCDContext *ctx);

void AudioCDPlayTrack(AudioCDContext *ctx, int track_number);
void AudioCDStop(AudioCDContext *ctx);
void AudioCDEject(AudioCDContext *ctx);


#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#define MAX(a,b)        ((a) > (b) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


/*
 *	Initializes the audio CD and creates a new context.  This does
 *	not need to be called between audio CD changes.
 */
AudioCDContext *AudioCDInit(void)
{
	AudioCDContext *ctx = (AudioCDContext *)calloc(
	    1, sizeof(AudioCDContext)
	);
	if(ctx == NULL)
	    return(NULL);

	ctx->prog = strdup("/usr/bin/cdplay");
	ctx->device = strdup("/dev/cdrom");
	ctx->current_track_number = 0;

	return(ctx);
}

/*
 *	Shutsdown the audio cd and deletes the context.
 */
void AudioCDShutdown(AudioCDContext *ctx)
{
	if(ctx == NULL)
	    return;

	free(ctx->prog);
	free(ctx->device);
	free(ctx);
}

/*
 *	Returns a list of audio CD tracks.
 */
AudioCDTrack **AudioCDListTracks(AudioCDContext *ctx, int *total)
{
	char *path;
	pid_t p;
	char cmd[PATH_MAX + NAME_MAX + 1024];
	AudioCDTrack **track = NULL, *t;


	if(total == NULL)
	    return(track);
	else
	    *total = 0;

	if(ctx == NULL)
	    return(track);

	if((ctx->prog == NULL) || (ctx->device == NULL))
	    return(track);


	/* format audio CD tracks command. */
	sprintf(
	    cmd,
	    "%s -c \"%s\" -n table",
	    ctx->prog, ctx->device
	);

	/* Get tempory file path. */
	path = tempnam(NULL, "stdo");
	if(path == NULL)
	    return(track);

	/* Execute command. */
	p = ExecBO(cmd, path);
	if(p > 0)
	{
	    /* Command executed successfully. */
	    int i, line_num = 0;
	    char *buf = NULL;
	    FILE *fp = FOpen(path, "rb");

	    /* Begin reading all lines from tempory output file. */
	    while(1)
	    {
		/* Deallocate previous line and read next line. */
		free(buf);
		buf = FGetStringLiteral(fp);
		if(buf == NULL)
		    break;

		/* Skip header (first 4 lines). */
		if(line_num > 4)
		{
		    int hours, minutes, seconds;
		    const char *s = buf;

		    /* Seek s to first argument. */
		    while(ISBLANK(*s))
			s++;
		    /* Skip '*' character infront of current track. */
		    if(*s == '*')
		    {
			s++;
			while(ISBLANK(*s))
			    s++;
		    }

		    /* Allocate a new track structure. */
		    i = *total;
		    *total = i + 1;
		    track = (AudioCDTrack **)realloc(
			track, (*total) * sizeof(AudioCDTrack *)
		    );
		    if(track == NULL)
		    {
			*total = 0;
			continue;
		    }
		    track[i] = t = (AudioCDTrack *)calloc(
			1, sizeof(AudioCDTrack)
		    );
		    if(t == NULL)
			continue;

		    /* Begin parsing line. */

		    /* Track number. */
		    t->number = MAX(atoi(s), 1);
		    /* Seek s to next argument. */
		    while(!ISBLANK(*s) && (*s != '\0'))
			s++;
		    while(ISBLANK(*s))
			s++;

		    /* Track start. */
		    hours = atoi(s);
		    while((*s != ':') && (*s != '\0'))
			s++;
		    if(*s == ':')
			s++;
		    minutes = atoi(s);
		    while((*s != ':') && (*s != '\0'))
			s++;
		    if(*s == ':')
			s++;
		    seconds = atoi(s);
		    t->start_time = (hours * 3600) + (minutes * 60) + seconds;
		    /* Seek s to next argument. */
		    while(!ISBLANK(*s) && (*s != '\0'))
			s++;
		    while(ISBLANK(*s))
			s++;

		    /* Track length. */
		    minutes = atoi(s);
		    while((*s != ':') && (*s != '\0'))
			s++;
		    if(*s == ':')
			s++;
		    seconds = atoi(s);
		    t->length = (minutes * 60) + seconds;
		    /* Seek s to next argument. */
		    while(!ISBLANK(*s) && (*s != '\0'))
			s++;
		    while(ISBLANK(*s))
			s++;

		    /* Name (last argument). */
		    t->name = strdup(s);
		}

		line_num++;
	    }

	    /* Close tempory output file. */
	    FClose(fp);
	}

	/* Delete tempory file path. */
	if(path != NULL)
	{
	    unlink(path);
	    free(path);
	    path = NULL;
	}

	return(track);
}

/*
 *	Deletes the list of audio CD track list.
 */
void AudioCDDeleteTracksList(AudioCDTrack **track, int total)
{
	int i;
	AudioCDTrack *t;

	for(i = 0; i < total; i++)
	{
	    t = track[i];
	    if(t == NULL)
		continue;

	    free(t->name);
	    free(t);
	}
	free(track);
}


/*
 *	Returns the track number that was last played or 0 if stop
 *	or eject was called.
 */
int AudioCDLastTrack(AudioCDContext *ctx)
{
	return((ctx != NULL) ? ctx->current_track_number : 0);
}


/*
 *	Plays the track specified by track_number.
 */
void AudioCDPlayTrack(AudioCDContext *ctx, int track_number)
{
	pid_t p;
	char cmd[PATH_MAX + NAME_MAX + 1024];


	if((ctx == NULL) || (track_number < 1))
	    return;

	if((ctx->prog == NULL) || (ctx->device == NULL))
	    return;

	/* format audio CD tracks command. */
	sprintf(
	    cmd,
	    "%s -c \"%s\" play %i",
	    ctx->prog, ctx->device, track_number
	);

	/* Execute command. */
	p = Exec(cmd);
	if(p > 0)
	    ctx->current_track_number = track_number;
}

/*
 *	Stop playing.
 */
void AudioCDStop(AudioCDContext *ctx)
{
	pid_t p;
	char cmd[PATH_MAX + NAME_MAX + 1024];


	if(ctx == NULL)
	    return;

	if((ctx->prog == NULL) || (ctx->device == NULL))
	    return;

	/* format audio CD tracks command. */
	sprintf(
	    cmd,
	    "%s -c \"%s\" -n stop",
	    ctx->prog, ctx->device
	);

	/* Execute command. */
	p = Exec(cmd);
	if(p > 0)
	    ctx->current_track_number = 0;
}

/*
 *	Ejects audio CD from the device.
 */
void AudioCDEject(AudioCDContext *ctx)
{
	pid_t p;
	char cmd[PATH_MAX + NAME_MAX + 1024];


	if(ctx == NULL)
	    return;

	if((ctx->prog == NULL) || (ctx->device == NULL))
	    return;

	/* format audio CD tracks command. */
	sprintf(
	    cmd,
	    "/usr/bin/eject -r \"%s\"",
	    ctx->device
	);

	/* Execute command. */
	p = Exec(cmd);
	if(p > 0)
	    ctx->current_track_number = 0;
}

