#include "tn_bs.h"
#include "TN_BS.bin_rev.h"
#include "Module.h"
#include "Player.h"

struct ExecIFace *IExec;
struct Interface *INewlib;
struct Library *NewlibBase;

struct TNDecHandle *handlePtr;
Player *player = NULL;
Module *module = NULL;
BOOL is_playing;
BOOL must_exit;

const char verstag[] = VERSTAG;

static int OpenLibs ();
static void CloseLibs ();

static uint32 GetLength ();
int write_sound (uint8 *sound, int bytes);

extern "C" int _start (char *argstr, int32 arglen, struct ExecBase *SysBase) {
	struct Process *proc;
	struct TNDecHandle *handle;
	uint32 signals;
	int rc = 20;

	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;
	
	if (!OpenLibs()) goto out;

	module = new Module(handle->modptr);
	if (!module) goto out;

	player = new Player(module, "", TRUE);
	if (!player) goto out;

	if (player->Start() != BS_OK) goto out;

	handle->length = GetLength();
	//handle->step = -1;
	handle->datasig = 1 << IExec->AllocSignal(-1);
	handle->endsig = 1 << IExec->AllocSignal(-1);

	IExec->ReplyMsg(&handle->msg);

	handle->data_in_buffer = 0;
	is_playing = TRUE;
	must_exit = FALSE;

	rc = 0;

	while (is_playing) {
		is_playing = player->Run();
		player->Mix();
	}
	while (!must_exit) {
		is_playing = TRUE;
		write_sound(NULL, 0);
	}

out:
	dbug(("leaving\n"));
	handle->player_task = NULL;
	delete(player);
	delete(module);
	CloseLibs();
	return rc;
}

static uint32 GetLength () {
	int stepptr, nextstep = -1;
	int patptr;
	short speed = 6, voice;
	uint16 pattern;
	uint8 option, data;
	uint32 ticks = 0;
	int count = 0;
	for (stepptr = 0; stepptr < module->GetNumSteps();) {
		for (patptr = 0; patptr < NOTESPERPATTERN; patptr++) {
			ticks += speed;
			for (voice = 0; voice < VOICES; voice++) {
				pattern = module->GetPattern(stepptr, voice);
				option = module->GetFX(pattern, patptr);
				data = module->GetFXByte(pattern, patptr);
				switch (option) {
					case 0x2:
						speed = data;
						if (!speed) speed++;
						break;
					case 0x7:
						nextstep = data;
						break;
				}
			}
		}
		if (nextstep != -1) {
			if (nextstep == 0) break;
			stepptr = nextstep;
			nextstep = -1;
		} else
			stepptr++;
		count++;
		if (count > 10000) return 0;
	}
	return ticks * 20;
}

int write_sound (uint8 *sound, int bytes) {
	struct TNDecHandle *handle = handlePtr;
	uint32 signals;
	int16 *pcm;
	uint32 i;

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

	if (signals & handle->endsig) {
		handle->player_task = NULL;
		is_playing = FALSE;
		must_exit = TRUE;
		return 0;
	}

	/*if (handle->step != -1) {
		player->SetStep(handle->step);
		handle->step = -1;
		return 0;
	}*/

	pcm = handle->pcm;
	for (i = 0; i < bytes; i++) {
		*pcm++ = ((*sound++)+128) << 8;
	}

	handle->data_in_buffer = bytes << 1;
	IExec->Signal(handle->main_task, SIGF_CHILD);

	return bytes;
}

static int OpenLibs () {
	NewlibBase = IExec->OpenLibrary("newlib.library", 52);
	INewlib = IExec->GetInterface(NewlibBase, "main", 1, NULL);
	if (!INewlib) return FALSE;
	return TRUE;
}

static void CloseLibs () {
	IExec->DropInterface(INewlib);
	IExec->CloseLibrary(NewlibBase);
}
