#include "Header.h"
#include "FFX.h"
#include "FSong.h"
#include "FSysLog.h"
#include "FTables.h"
#include "GlobHdr.h"
#include "Mem.h"
#include "TimLib:Seq.h"
#include "TimLib:SeqHdr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

GlobHdr* Glb = NULL;
#ifndef MAKEABS
static const _kernel_oserror Err_WrongLockCode =
{ErrNum_ParamsError, "Lock code is incorrect"};
static const _kernel_oserror Err_LockedNoExit =
{ErrNum_PlayerError, "Module cannot be killed while locked"};
static const _kernel_oserror unknown_conversion_code =
{ErrNum_ParamsError, "Unknown conversion code."};
#endif

_kernel_oserror* Module_Initialize(const char* cmd_tail, int podule_base, void* pw)
{
	const _kernel_oserror* e;

	IGNORE(cmd_tail);
	IGNORE(podule_base);

	Glb = calloc(1, sizeof(*Glb));
	Glb->pw = pw;
	Glb->pmodname = Module_Title;
	Glb->emulvolume = 256;
#ifndef MAKEABS
	e = CMem_Init(Glb, 64*1024*1024);
#else
	e = CMem_Init(Glb, 0);
#endif
	if (!e)
	{
		e = CSeq_Init(Glb);
		if (!e) e = Songs_Init(Glb, pw);
#ifndef MAKEABS
		if (!e) e = FXs_Init(Glb, pw);
#endif
		if (!e) e = SysLog_Init(Glb, pw);
#ifndef MAKEABS
		if (!e) Module_LoadConfig(Glb, NULL);
#endif

		if (e) CMem_Finalize(Glb);
	}

	return (_kernel_oserror*) e;
}

_kernel_oserror* Module_Finalize(int fatal, int podule, void* pw)
{
	const _kernel_oserror* e = NULL;

	IGNORE(fatal);
	IGNORE(podule);

#ifndef MAKEABS
	if (Glb->lock != 0)
		e = &Err_LockedNoExit;
#endif
	if (!e) e = CSeq_AllowFinalize(Glb);
	if (!e) e = SysLog_Finalize(Glb, pw);
#ifndef MAKEABS
	if (!e) e = FXs_Finalize(Glb, pw);
#endif
	if (!e) e = Songs_Finalize(Glb, pw);
	if (e) return (_kernel_oserror*) e;

	CSeq_Finalize(Glb);
	CMem_Finalize(Glb);

	free(Glb);
	Glb = NULL;

	return NULL;
}

#ifndef MAKEABS
static const _kernel_oserror* swi_Version(GlobHdr* g, _kernel_swi_regs* r)
{
	IGNORE(g);

	r->r[0] = Module_VersionNumber;

	return NULL;
}

#define MAX_CONFIG 16
static const int config_codes[MAX_CONFIG + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 256, 515};

/*
 * Module_LoadConfig
 */
const _kernel_oserror* Module_LoadConfig(GlobHdr* g, const char* pfilename)
{
	const _kernel_oserror* e = NULL;
	int fhandle = 0;
	char buf[64];
	char* pfree = buf;
	char* pend = buf + sizeof(buf);
	int len = 0;
	_kernel_swi_regs ri;

	if (pfilename == NULL) pfilename = "Choices:TimPlayer";
	e = _swix(OS_Find, _INR(0,1)|_OUT(0), 0x4f, pfilename, &fhandle);
	if (e) return e;

	while (((e == NULL) && (len == 0)) || (pend > buf))
	{
		int clen = sizeof(buf) - (pfree - buf);
		e = _swix(OS_GBPB, _INR(0,3)|_OUT(3), 4, fhandle, pfree, clen, &len);
		pend = buf + sizeof(buf) - len;
		for (pfree = buf; pfree < pend; pfree++)
		{
			if ('\n' == *pfree)
			{
				*pfree = 0;
				pfree++;
				break;
			}
		}

		if (pfree > buf)
		{
			// scan string and set config
			if (buf[0] == '7')
				sscanf(buf, "%x;%x;%x;%x;%x;%x;%x"
					, &ri.r[0], &ri.r[1], &ri.r[2], &ri.r[3], &ri.r[4], &ri.r[5], &ri.r[6]);
			else
				sscanf(buf, "%x;%x;", &ri.r[0], &ri.r[1]);

			// upate only if in allowed list
			for (int i = MAX_CONFIG; i >= 0; i--)
			{
				if (config_codes[i] == ri.r[0])
					swi_Configure(g, &ri);
			}

			// move reminder to start of buffer
			memmove(buf, pfree, pend - pfree);
		}
		pfree = buf + (pend - pfree);
	}

	// Close file
	if (fhandle != 0)
	{
		e = _swix(OS_Find, _INR(0, 1), 0, fhandle);
		if (e) return e;
	}

	return NULL;
}

/*
 * Module_SaveConfig
 */
const _kernel_oserror* Module_SaveConfig(GlobHdr* g, const char* pfilename)
{
	const _kernel_oserror* e = NULL;
	int fhandle = 0;
	char buf[64];
	_kernel_swi_regs ri;

	if (pfilename == NULL) pfilename = "<Choices$Write>.TimPlayer";

	e = _swix(OS_Find, _INR(0,1)|_OUT(0), 0x80, pfilename, &fhandle);
	if (e) return e;

	for (int i = MAX_CONFIG; i >= 0; i--)
	{
		ri.r[0] = config_codes[i];

		if (ri.r[0] == 3)
		{
			ri.r[1] = (int)(0x1u << 31);
			swi_Configure(g, &ri);
			snprintf(buf, sizeof(buf), "%x;%x\n", ri.r[0], ri.r[1]);
			_swix(OS_GBPB, _INR(0,3), 2, fhandle, buf, strlen(buf));
		}
		else if (ri.r[0] == 7)
		{
			static const BandPass empty = {0, 0, 0, 0, 0};

			for (int j = 0; j < Max_Equalizers; j++)
			{
				const BandPass* b = (g->seqdataptr->EqualNr) ? &g->seqdataptr->EqualPars[j] : &empty;
				snprintf(buf, sizeof(buf), "%x;%x;%x;%x;%x;%x;%x\n"
					, ri.r[0], j, b->par_c0, b->par_c1, b->par_c2, b->par_d1, b->par_d2);
				_swix(OS_GBPB, _INR(0,3), 2, fhandle, buf, strlen(buf));
			}
		}
		else
		{
			ri.r[1] = -1;
			swi_Configure(g, &ri);
			snprintf(buf, sizeof(buf), "%x;%x\n", ri.r[0], ri.r[1]);
			_swix(OS_GBPB, _INR(0,3), 2, fhandle, buf, strlen(buf));
		}
	}

	// Close file
	if (fhandle != 0)
	{
		e = _swix(OS_Find, _INR(0, 1), 0, fhandle);
		if (e) return e;
		// Give it type Text
		_swix(8, _INR(0,2), 18, pfilename, 0xfff);
	}

	return NULL;
}
#endif

/*
 * SWI Configure
 *
 * In  - R0 code
 *       R1 value
 *
 * Out - R1 current value
 */
const _kernel_oserror* swi_Configure(GlobHdr* g, _kernel_swi_regs* r)
{
#ifndef MAKEABS
	switch(r->r[0])
	{
		case -1: // close all streams, unlock choices
		{
			FXs_UnregisterAll(g);
			Songs_UnloadAll(g);
			g->lock = 0;
			return NULL;
		}
		break;
		case 512: // save choices
		{
			return Module_SaveConfig(g, (const char*) r->r[1]);
		}
		break;
		case 513: // load choices
		{
			return Module_LoadConfig(g, (const char*) r->r[1]);
		}
		break;
		case 514: // lock/unlock choices
		{
			if (r->r[1] == 0)
			{
				if (g->lock != 0)
				{
					if (g->lock == r->r[2])
						g->lock = 0;
					else
					    return &Err_WrongLockCode;
				}
			}
			else if (r->r[1] == 1)
			{
				if (g->lock == 0)
					g->lock = r->r[2];
			}


			r->r[1] = (g->lock != 0);

			return NULL;
		}
		break;
		case 515: // emulation scale volume
		{
			if ((g->lock == 0) && (r->r[1] >= 0) && (r->r[1] <= 256))
				g->emulvolume = r->r[1];
			r->r[1] = g->emulvolume;
			return NULL;
		}
	}

	if (g->lock != 0)
	{
		// turn writes into reads
		if (r->r[0] == 3)
			r->r[1] = (int)(0x1u << 31);
		else if (r->r[0] == 7)
			return NULL;
		else if (r->r[0] != 255)
			r->r[1] = -1;
	}
#endif

	if ((r->r[0] < 128) || (r->r[0] == 255))
		return CSeq_Configure(g, r);
	else
		return swi_Configure_Songs(g, r);
}

/*
 * SWI Convert
 *
 * In  - R0 code
 *       R1... values
 *
 * Out - ...
 */
#ifndef MAKEABS
static const _kernel_oserror* swi_Convert(GlobHdr* g, _kernel_swi_regs* r)
{
	IGNORE(g);

	switch(r->r[0])
	{
		case 0: r->r[1] = Convert_PeriodToTone(r->r[1]); break;
		case 1: r->r[1] = Convert_ToneToPeriod(r->r[1]); break;
		case 2: r->r[1] = Convert_FracToTone(r->r[1]); break;
		case 3: r->r[1] = Convert_ToneToFrac(r->r[1]); break;
		case 4: r->r[1] = Convert_FracToPeriod(r->r[1]); break;
		case 5: r->r[1] = Convert_PeriodToFrac(r->r[1]); break;
		case 6: r->r[1] = Convert_RelTone(r->r[1], r->r[2]); break;
		case 7: r->r[1] = (int) Seq_GetLogTable(); break;
		default: return &unknown_conversion_code;
	}

	return NULL;
}

void Module_Service(int service_number, _kernel_swi_regs* r, void* pw)
{
	static const char Acknowlegments[] = "Thanks to Jeffrey Lim for the IT samples decompression\nroutines and to John Tytgat for the Digital Symphony ones.";

	IGNORE(pw);

	switch (service_number)
	{
		case 0x54:
		{
			CSeq_SoundService(Glb, r);
		}
		break;
		case 0x42680:
		{
			r->r[0] = 0x4000;
			r->r[1] = (int) Module_Title;
			r->r[2] = (int) Acknowlegments;
		}
		break;
	}
}

_kernel_oserror* Module_Swis(int swi_offset, _kernel_swi_regs* r, void* pw)
{
	IGNORE(pw);

	switch(swi_offset)
	{
		case 0x00: return (_kernel_oserror*) swi_Version(Glb, r);
		case 0x01: return (_kernel_oserror*) swi_Configure(Glb, r);
		case 0x02: return (_kernel_oserror*) swi_Song_Load(Glb, r);
		case 0x03: return (_kernel_oserror*) swi_Song_Unload(Glb, r);
		case 0x04: return (_kernel_oserror*) swi_Song_New(Glb, r);
		case 0x05: return (_kernel_oserror*) swi_Song_Load2(Glb, r);
		case 0x06: return (_kernel_oserror*) swi_Song_Decompress(Glb, r);
		case 0x07: return (_kernel_oserror*) swi_Convert(Glb, r);

		case 0x08: return (_kernel_oserror*) swi_Song_Play(Glb, r);
		case 0x09: return (_kernel_oserror*) swi_Song_Pause(Glb, r);
		case 0x0a: return (_kernel_oserror*) swi_Song_Stop(Glb, r);
		case 0x0b: return (_kernel_oserror*) swi_Song_Position(Glb, r);
		case 0x0c: return (_kernel_oserror*) swi_Song_Volume(Glb, r);
		case 0x0d: return (_kernel_oserror*) swi_Song_Status(Glb, r);
		case 0x0e: return (_kernel_oserror*) swi_Song_Configure(Glb, r);
		case 0x0f: return (_kernel_oserror*) swi_Song_PlayRange(Glb, r);

		case 0x10: return (_kernel_oserror*) swi_Song_Info(Glb, r);
		case 0x11: return (_kernel_oserror*) swi_Song_Texts(Glb, r);
		case 0x12: return (_kernel_oserror*) swi_Song_InitialSettings(Glb, r);
		case 0x13: return (_kernel_oserror*) swi_Channel_InitialSettings(Glb, r);
		case 0x14: return (_kernel_oserror*) swi_Song_PlayInfo(Glb, r);
		case 0x15: return (_kernel_oserror*) swi_Song_SectionInfo(Glb, r);

		case 0x18: return (_kernel_oserror*) swi_Pattern_Info(Glb, r);
		case 0x19: return (_kernel_oserror*) swi_Row_Info(Glb, r);

		case 0x1f: return (_kernel_oserror*) swi_FX_Status(Glb, r);

		case 0x20: return (_kernel_oserror*) swi_Sample_Info(Glb, r);
		case 0x21: return (_kernel_oserror*) swi_Sample_Misc(Glb, r);
		case 0x22: return (_kernel_oserror*) swi_Sample_Loops(Glb, r);
		case 0x23: return (_kernel_oserror*) swi_Sample_Vibrato(Glb, r);

		case 0x27: return (_kernel_oserror*) swi_Instrument_Vibrato(Glb, r);
		case 0x28: return (_kernel_oserror*) swi_Instrument_Info(Glb, r);
		case 0x29: return (_kernel_oserror*) swi_Instrument_Volume(Glb, r);
		case 0x2a: return (_kernel_oserror*) swi_Instrument_Panning(Glb, r);
		case 0x2b: return (_kernel_oserror*) swi_Instrument_Filter(Glb, r);
		case 0x2c: return (_kernel_oserror*) swi_Instrument_VolumeEnvelope(Glb, r);
		case 0x2d: return (_kernel_oserror*) swi_Instrument_PanningEnvelope(Glb, r);
		case 0x2e: return (_kernel_oserror*) swi_Instrument_PitchEnvelope(Glb, r);
		case 0x2f: return (_kernel_oserror*) swi_Instrument_FilterEnvelope(Glb, r);

		case 0x30: return (_kernel_oserror*) swi_FX_Register(Glb, r);
		case 0x31: return (_kernel_oserror*) swi_FX_Unregister(Glb, r);
		case 0x32: return (_kernel_oserror*) swi_FX_GlobalSettings(Glb, r);
		case 0x33: return (_kernel_oserror*) swi_FX_ChannelSettings(Glb, r);
		case 0x34: return (_kernel_oserror*) swi_FX_NoteSettings(Glb, r);
		case 0x35: return (_kernel_oserror*) swi_FX_PlaySample(Glb, r);
		case 0x36: return (_kernel_oserror*) swi_FX_PlayInstrument(Glb, r);
		case 0x37: return (_kernel_oserror*) swi_FX_NoteAction(Glb, r);

		case 0x38: return (_kernel_oserror*) swi_Song_SongParams(Glb, r);
		case 0x39: return (_kernel_oserror*) swi_Song_ChannelParams(Glb, r);
		case 0x3a: return (_kernel_oserror*) swi_Song_VChannelParams(Glb, r);
		case 0x3b: return (_kernel_oserror*) swi_Channel_Status(Glb, r);
		case 0x3c: return (_kernel_oserror*) swi_FX_ChannelParams(Glb, r);
		case 0x3d: return (_kernel_oserror*) swi_FX_VChannelParams(Glb, r);
		case 0x3e: return (_kernel_oserror*) swi_Channel_Variable(Glb, r);
		case 0x3f: return (_kernel_oserror*) CSeq_GetMixingInfo(Glb, r);
	}

	return error_BAD_SWI;
}
#endif
