#include "tn_gsf.h"
#include "TN_GSF.bin_rev.h"

struct ExecIFace *IExec;
struct DOSIFace *IDOS;
struct UtilityIFace *IUtility;
struct Interface *INewlib;
struct Library *DOSBase;
struct Library *UtilityBase;
struct Library *NewlibBase;

struct TNDecHandle *handlePtr;
BOOL is_playing;
BOOL must_exit;

extern int LengthFromString (const char *timestring);

int defvolume=1000;
int relvolume=1000;
int TrackLength=0;
int FadeLength=0;
int IgnoreTrackLength=0, DefaultLength=150000;
int playforever=0;
int fileoutput=0;
int TrailingSilence=1000;
int DetectSilence=1, silencedetected=0, silencelength=5;

int cpupercent = 0;
int sndSamplesPerSec, sndNumChannels;
int sndBitsPerSample = 16;

int deflen=120,deffade=4;

double decode_pos_ms; // current decoding position, in milliseconds

const char verstag[] = VERSTAG;

int OpenLibs ();
void CloseLibs ();

void psfTag (struct TNDecHandle *handle);
void writeSound (uint8 *sound, uint32 bytes);

int _start (char *argstr, int32 arglen, struct ExecBase *SysBase) {
	struct Process *proc;
	struct TNDecHandle *handle;
	uint32 signals;

	dbug(("waiting for startup msg\n"));
	IExec = (struct ExecIFace *)SysBase->MainInterface;
	proc = (struct Process *)IExec->FindTask(NULL);
	IExec->WaitPort(&proc->pr_MsgPort);
	handle = (struct TNDecHandle *)IExec->GetMsg(&proc->pr_MsgPort);
	handlePtr = handle;
	
	dbug(("opening libraries\n"));
	if (!OpenLibs()) {
		dbug(("leaving\n"));
		CloseLibs();
		return 20;
	}

	dbug(("opening psf file\n"));
	if (!GSFRun(handle->filename)) {
		dbug(("leaving\n"));
		CloseLibs();
		return 20;
	}

	handle->datasig = 1 << IExec->AllocSignal(-1);
	handle->endsig = 1 << IExec->AllocSignal(-1);

	psfTag(handle);
	handle->info.channels = sndNumChannels;
	handle->info.sample_rate = sndSamplesPerSec;
	handle->info.length = TrackLength;

	dbug(("replying\n"));
	IExec->ReplyMsg(&handle->msg);

	dbug(("entering main loop\n"));
	handle->data_in_buffer = 0;
	is_playing = TRUE;
	must_exit = FALSE;
	decode_pos_ms = 0.0;

	while (!must_exit) {
		if (!is_playing) {
			is_playing = TRUE;
			decode_pos_ms = 0.0;
			writeSound(NULL, 0);
			if (!GSFRun(handle->filename)) break;
		}
		EmulationLoop();
	}

	handle->player_task = NULL;
	dbug(("leaving\n"));
	GSFClose();
	IExec->FreeVec(handle->info.title);
	IExec->FreeVec(handle->info.game);
	IExec->FreeVec(handle->info.comment);
	CloseLibs();
	return 0;
}

void psfTag (struct TNDecHandle *handle) {
	char tag[50001];
	char tmp_str[256];
	psftag_readfromfile((void *)tag, handle->filename);
	if (!psftag_getvar(tag, "title", tmp_str, sizeof(tmp_str)-1)) {
		handle->info.title = IUtility->ASPrintf("%s", tmp_str);
	}
	if (!psftag_getvar(tag, "game", tmp_str, sizeof(tmp_str)-1)) {
		handle->info.game = IUtility->ASPrintf("%s", tmp_str);
	}
	if (!psftag_getvar(tag, "comment", tmp_str, sizeof(tmp_str)-1)) {
		handle->info.comment = IUtility->ASPrintf("%s", tmp_str);
	}
	if (!psftag_raw_getvar(tag, "fade", tmp_str, sizeof(tmp_str)-1)) {
		FadeLength = LengthFromString(tmp_str);
	}
	if (!psftag_raw_getvar(tag, "length", tmp_str, sizeof(tmp_str)-1)) {
		TrackLength = LengthFromString(tmp_str) + FadeLength;
	}
}

void end_of_track() {
	is_playing = FALSE;
}

void writeSound (uint8 *sound, uint32 bytes) {
	struct TNDecHandle *handle = handlePtr;
	uint32 signals;
	uint32 send_bytes;

	if (must_exit || !is_playing) return;
	signals = IExec->Wait(handle->datasig|handle->endsig);

	if (signals & handle->endsig) {
		dbug(("end signal\n"));
		must_exit = TRUE;
		return;
	}

	if (bytes < AUDIO_BUFFER_SIZE) {
		send_bytes = bytes;
	} else {
		send_bytes = AUDIO_BUFFER_SIZE;
	}

	dbug(("sending audio (%ld bytes)\n", send_bytes));
	IExec->CopyMem(sound, handle->pcm, send_bytes);
	handle->data_in_buffer = send_bytes;
	IExec->Signal(handle->main_task, SIGF_CHILD);

	if (bytes > AUDIO_BUFFER_SIZE) {
		bytes -= AUDIO_BUFFER_SIZE;
		dbug(("more data than could fit into buffer (%ld bytes)\n", bytes));
		writeSound(sound + send_bytes, bytes);
	}
}

int OpenLibs () {
	DOSBase = IExec->OpenLibrary("dos.library", 52);
	IDOS = (struct DOSIFace *)IExec->GetInterface(DOSBase, "main", 1, NULL);
	if (!IDOS) return FALSE;
	UtilityBase = IExec->OpenLibrary("utility.library", 52);
	IUtility = (struct UtilityIFace *)IExec->GetInterface(UtilityBase, "main", 1, NULL);
	if (!IUtility) return FALSE;
	NewlibBase = IExec->OpenLibrary("newlib.library", 52);
	INewlib = IExec->GetInterface(NewlibBase, "main", 1, NULL);
	if (!INewlib) return FALSE;
	return TRUE;
}

void CloseLibs () {
	IExec->DropInterface(INewlib);
	IExec->CloseLibrary(NewlibBase);
	IExec->DropInterface((struct Interface *)IUtility);
	IExec->CloseLibrary(UtilityBase);
	IExec->DropInterface((struct Interface *)IDOS);
	IExec->CloseLibrary(DOSBase);
}
