#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
extern int errno;
#include <signal.h>
#include <sched.h>

#ifdef HAVE_LIBKMID
# include <libkmid/libkmid.h>
#endif  /* HAVE_LIBKMID */

#include "../include/string.h"
#include "../include/prochandle.h"

#include "midi.h"
#include "midiiow.h"

#include "options.h"


int MIDIIOWInit(MIDIAudio *midi_audio);
pid_t MIDIIOWPlay(MIDIAudio *midi_audio, const char *path);
void MIDIIOWStop(MIDIAudio *midi_audio);
pid_t MIDIIOWIsPlaying(MIDIAudio *midi_audio);
void MIDIIOWShutdown(MIDIAudio *midi_audio);


#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))


/*
 *	Midi Play ID, for use with keeping track of the midi play
 *	process using the standard forking of the midi playing program
 *	(if HAVE_LIBKMID is not defined).
 *
 *	If HAVE_LIBKMID is defined, then this contains a unique play
 *	ID.
 */
static pid_t midi_play_pid = 0;


#if !defined(HAVE_LIBKMID)
/*
 *	Checks if the process specified by pid is still running,
 *	returns true if it is or false if it is not.
 */
int MIDIIOWProcessExist(pid_t pid);
#endif	/* !defined(HAVE_LIBKMID) */



#if !defined(HAVE_LIBKMID)
int MIDIIOWProcessExist(pid_t pid)
{
	struct sched_param sp;


	/* Cannot be 0 (because it means itself to sched_getparam()). */
	if(pid == 0)
	    return(0);

	if(sched_getparam(
	    pid,
	    &sp
	) == 0)
	    return(1);
	else
	    return(0);
}
#endif  /* !defined(HAVE_LIBKMID) */



/*
 *	Initializes MIDI IO resources.
 *
 *	This should be called when the recorder is initialized.
 */
int MIDIIOWInit(MIDIAudio *midi_audio)
{
	if(midi_audio == NULL)
	    return(-1);

#ifdef HAVE_LIBKMID

	if(kMidInit())
	{
	    fprintf(stderr,
 "kMidInit(): Initialization failure, midi support will be unavailable.\n"
	    );
	    return(-1);
	}
	else
	{
	    printf(
		"libkmidi: Initialized device number %i.\n",
		midi_audio->midi_device_number
	    );
	}

	/* Set MIDI device number. */
	kMidSetDevice(midi_audio->midi_device_number);

	/* Set lib data pointer to 1 just so that it's not
	 * checked to be NULL by other functions that use this
	 * value to determine if the library is initialized or not.
	 */
	midi_audio->ptr = (void *)1;

#else	/* HAVE_LIBKMID */

	/* Set lib data pointer to 1 just so that it's not
	 * checked to be NULL by other functions that use this
	 * value to determine if the library is initialized or not.
	 */
	midi_audio->ptr = (void *)1;

#endif

	/* Reset midi_play_pid. */
	midi_play_pid = 0;

	return(0);
}

/*
 *	Plays the MIDI file specified by path, returns a unique
 *	id to identify the play or 0 on failure.
 */
pid_t MIDIIOWPlay(MIDIAudio *midi_audio, const char *path)
{
	if(path == NULL)
	    return(0);

	if(midi_audio == NULL)
	    return(0);

#ifdef HAVE_LIBKMID

	if(midi_audio->ptr != NULL)
	{
	    /* Stop incase it is already playing. */
	    kMidStop();

	    /* Load and play new MIDI file. */
	    kMidLoad(path);
	    kMidPlay();

	    /* Increment midi_play_pid. */
	    midi_play_pid++;

	    return(midi_play_pid);
	}
	else
	{
	    return(0);
	}

#else	/* HAVE_LIBKMID */

	if(midi_audio->ptr != NULL)
	{
	    int len;
	    char *cmd;

	    /* Stop incase it is already playing. */
	    if(midi_play_pid > 0)
		MIDIIOWStop(midi_audio);


	    /* Allocate and format play command. */
	    len = strlen(path) +
		strlen(option.midi_play_cmd) + 256;
	    cmd = (char *)malloc((len + 1) * sizeof(char));
	    if(cmd == NULL)
		return(0);

	    strcpy(cmd, option.midi_play_cmd);
	    substr(cmd, "%f", path);

	    /* Execute command and record midi play pid. */
	    midi_play_pid = Exec(cmd);
	    if(midi_play_pid == 0)
		return(0);

	    /* Free command. */
	    free(cmd);

	    return(midi_play_pid);
	}
	else
	{
	    return(0);
	}

#endif
}

/*
 *	Stop playing MIDI if it is still playing.
 */
void MIDIIOWStop(MIDIAudio *midi_audio)
{
       if(midi_audio == NULL)
	    return;

#ifdef HAVE_LIBKMID

	if(midi_audio->ptr != NULL)
	{
	    /* Stop if currently playing. */
	    kMidStop();
	}

#else	/* HAVE_LIBKMID */

	if(midi_audio->ptr != NULL)
	{
	    /* Stop any midi's still playing. */
	    if(midi_play_pid > 0)
		kill(midi_play_pid, SIGINT);

	    /* Reset midi_play_pid. */
	    midi_play_pid = 0;
	}

#endif

	return;
}

/*
 *	Checks if the MIDI is still being played, returns the pid
 *	of the process if it is still being played or 0 if it is not.
 */
pid_t MIDIIOWIsPlaying(MIDIAudio *midi_audio)
{
       if(midi_audio == NULL)
	    return(0);

#ifdef HAVE_LIBKMID

	if(midi_audio->ptr != NULL)
	{
	    int status = kMidIsPlaying();

	    if(status)
		return(midi_play_pid);
	    else
		return(0);
	}
	else
	{
	    return(0);
	}

#else   /* HAVE_LIBKMID */

	if(midi_audio->ptr != NULL)
	{
	    /*   Check if the midi play process is still running.
	     *   This will set midi_play_pid to 0 if the midi play
	     *   process no longer exists or leave midi_play_pid as
	     *   is if the midi play process is still active.
	     */
	    if(midi_play_pid > 0)
	    {
		if(!MIDIIOWProcessExist(midi_play_pid))
		    midi_play_pid = 0;
	    }

	    return(midi_play_pid);
	}
	else
	{
	    return(0);
	}

#endif
}

/*
 *	Shuts down all MIDI IO resources.
 *
 *	This should be called when the recorder is shut down.
 */
void MIDIIOWShutdown(MIDIAudio *midi_audio)
{
	if(midi_audio == NULL)
	    return;

#ifdef HAVE_LIBKMID

	if(midi_audio->ptr != NULL)
	{
	    /* Stop any midi's still playing. */
	    if(MIDIIOWIsPlaying(midi_audio))
		MIDIIOWStop(midi_audio);

	    /* Shutdown libkmid. */
	    kMidDestruct();

	    /* Reset midi_play_pid. */
	    midi_play_pid = 0;

	    /* Reset pointer to external resources (never
	     * allcoated).
	     */
	    midi_audio->ptr = NULL;
	}

#else	/* HAVE_LIBKMID */

	if(midi_audio->ptr != NULL)
	{
	    /* Stop any midi's still playing. */
	    if(midi_play_pid > 0)
		MIDIIOWStop(midi_audio);

	    /* Reset midi_play_pid (though MIDIIOWStop() probably
	     * already reset it).
	     */
	    midi_play_pid = 0;

	    /* Reset pointer to external resources (never
	     * allcoated).
	     */
	    midi_audio->ptr = NULL;
	}

#endif

	return;
}

