#include "Header.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "swis.h"
#include "GlobHdr.h"
#include "FStream.h"
#include "Log.h"

extern const _kernel_oserror* IStream_Lister(_kernel_swi_regs* r, IStream* s);

const _kernel_oserror* IStream_Lister(_kernel_swi_regs* r, IStream* s)
{
	r = r;
	s = s;
	return NULL;
}

static int Keyboard_EscPressed(void)
{
	int pa, pb;

	_swix(OS_Byte, _INR(0,2)|_OUTR(1,2), 129, 0, 0, &pa, &pb);
	if (!pb)
	{
		if (pa == 27)
			return 1;
	}

	return 0;
}

int main(int argc, char **argv)
{
	int t, tin, tout;
	int handle = -1;
	_kernel_swi_regs r;
	const _kernel_oserror* e = NULL;
	const char* Program_Name = argv[0];
	const char* p;
	const char* infile = NULL;
	const char* outfile = NULL;
	int n;
	int verbose = 7;
	int bOk;
	int isready = 0;
	int th2 = -1, tm2 = 0, ts2 = 0;
	int intype = 1;
	int cddevice = 0, cdstart = 0, cdend = 0;

	e = Module_Initialize(NULL, 0, NULL);
	if (e)
	{
		fprintf(stderr, "%s\n", e->errmess);
		return EXIT_FAILURE;
	}

	p = strrchr(Program_Name, ':');
	if (p) Program_Name = p + 1;
	p = strrchr(Program_Name, '.');
	if (p) Program_Name = p + 1;

	// Disable Escape
	_swix(OS_Byte, _INR(0,2), 0xe5, 1, 0);

	for (n = 1, bOk = 1; (n < argc) && bOk; n++)
	{
		char* p = argv[n];

		if (*p == '-')
		{
			if (p[1] == 'v')
			{
				verbose = atoi(p + 2);
				if (verbose < 0) verbose = 0;
			}
			else
				bOk = 0;
		}
		else if (!infile)
		{
			infile = p;
			if (!(strncmp(infile,"::CDFS",6)))
			{
				intype = 4;
				cddevice = atoi(p+6);
				p = strchr(p+6, ',');
				if (!p) bOk = 0;
				else
				{
					cdstart = atoi(p+1);
					p = strchr(p+1, ',');
					if (!p) bOk = 0;
					else
						cdend = atoi(p+1);
				}
			}
		}
		else if (!outfile)
			outfile = p;
		else
			bOk = 0;
	}

	LogVerbosity(verbose);

	// False loop for error catching
	while (!e)
	{
		if (!bOk || !infile)
		{
			fprintf(stderr, "%s v%s (%s) is a file to WAVE convertor.\n"
			      , Program_Name, Module_VersionString, Module_Date);
			fprintf(stderr, "Syntax: %s [options] <infile> [<outfile>]\n", Program_Name);
			fprintf(stderr, "Options:\n");
			fprintf(stderr, "-v<n> Verbosity: 0 Silent, 1 Input info, 2 Progress indicator\n");
			fprintf(stderr, "<infile> may be ::CDFS<x>,<first>,<last> to extract blocks <first> to <last> from CD drive <x>\n");
			break;
		}

		r.r[0] = 0;
		r.r[1] = 64;
		e = swi_StreamCreate(Glb, &r);
		if (e) break;
		handle = r.r[0];


		r.r[0] = handle;
		r.r[1] = 2;
		r.r[2] = (int) outfile;
		r.r[3] = 0;
		e = swi_StreamReceiver(Glb, &r);
		if (e) break;

		r.r[0] = handle;
		r.r[1] = intype;
		if (intype == 1)
		{
			struct
			{
				int size;
				const char* codecname;
				int flags;
			} desc = {0, NULL, 1};

			int filetype;
			r.r[2] = (int) infile;

			// Get file type
			e = _swix(OS_File, _INR(0,1)|_OUT(6), 23, infile, &filetype);
			if (e) break;
			// if type Data, by extension
			if (filetype == 0xffd)
			{
				const char* ext = strrchr(infile, '/');
				if (ext)
				{
					r.r[1] = intype = 3;
					r.r[3] = (int) &desc;
					desc.codecname = ext + 1;
				}
			}
			e = swi_StreamSource(Glb, &r);
			if (e) break;
		}
		else
		{
			r.r[2] = cddevice;
			r.r[3] = cdstart;
			r.r[4] = cdend;
			e = swi_StreamSource(Glb, &r);
			if (e) break;
		}

		// Wait (with a timeout) till it has extracted playing info (samplerate, channels, ...)
		t=clock(); tout = t + 10*CLK_TCK;

		while((clock() < tout) && !isready && !e)
		{
			IStream_BackgroundProcess(Glb, Glb->streamsptr[handle]);

			// We only have to wait till it's is ready
			r.r[0] = handle;
			e = swi_StreamIsReady(Glb, &r);
			if (e) break;
			isready = r.r[1];
		}

		if (e) break;

		// Play till end of source
		t = tin = clock();
		if (isready)
		{
			int th, tm, ts, pos, status;

			if (verbose)
			{
				int channels, freq, bitrate;
				const char* type;

				r.r[0] = handle;
				r.r[1] = 0;
				e = swi_StreamParam(Glb, &r);
				if (e) break;
				channels = r.r[2];

				r.r[0] = handle;
				r.r[1] = 1;
				e = swi_StreamParam(Glb, &r);
				if (e) break;
				freq = r.r[2];

				r.r[0] = handle;
				r.r[1] = 2;
				e = swi_StreamParam(Glb, &r);
				if (e) break;
				bitrate = r.r[2];

				r.r[0] = handle;
				r.r[1] = 3;
				e = swi_StreamParam(Glb, &r);
				if (e) break;
				type = (const char*) r.r[2];

				printf("File is %s (%d channels, %d Hz, %d kbit/s)\n\n"
					, type
					, channels
					, freq
					, bitrate);

				// Get stream probable duration info
				r.r[0] = handle;
				e = swi_StreamInfo(Glb, &r);
				if (e) break;
				pos = r.r[2];

				if (pos)
				{
					ts2 = pos /1000;
					tm2 = ts2 / 60; ts2 = ts2 % 60;
					th2 = tm2 / 60; tm2 = tm2 % 60;
					printf("Total Time: % 3d:%02d:%02d\n", th2, tm2, ts2);
				}
			}

			// Start to play
			r.r[0] = handle;
			e = swi_StreamPlay(Glb, &r);
			if (e) break;

			// Loop till the stream is completely played
			status = 2;
			while(!e && (status & 2))
			{
				IStream_BackgroundProcess(Glb, Glb->streamsptr[handle]);

				// Decoding and playing is done by DiskSample as a background process,
				// we only have to wait till it pauses at the end of the file.
				r.r[0] = handle;
				r.r[1] = 0;
				r.r[2] = 0;
				e = swi_StreamStatus(Glb, &r);
				if (e) break;
				status = r.r[1];

				// Show current playing time
				if (((clock() - t) > 100) || ((status & 2) == 0))
				{
					if (Keyboard_EscPressed())
					{
						printf("\nStop requested by user\n");
						break;
					}

					if (verbose > 1)
					{
						if ((status & 2) == 0)
						{
							if (th2 >= 0) printf("\rTime: % 3d:%02d:%02d", th2, tm2, ts2);
						}
						else
						{
							r.r[0] = handle;
							r.r[1] = -1;
							e = swi_StreamPosition(Glb, &r);
							if (e) break;
							pos = r.r[1];

							ts = pos /1000;
							tm = ts / 60; ts = ts % 60;
							th = tm / 60; tm = tm % 60;
							printf("\rTime: % 3d:%02d:%02d", th, tm, ts);
						}
					}

					t = clock();
				}
			}
			if (verbose > 1) printf("\n");

			t = clock() - tin;
			if (verbose)
			{
				printf("Decoded in %d cs\n", t);
			}
		}
		else
		{
			fprintf(stderr, "The source's content does not seem to be playable\n");
		}

		// End of decoding
		break;
	}

	if (e) fprintf(stderr, "Error: %s\n", e->errmess);

	if (handle != -1)
	{
		r.r[0] = handle;
		e = swi_StreamClose(Glb, &r);
		if (e) fprintf(stderr, "%s\n", e->errmess);
	}

	Module_Finalize(0, 0, Glb->pw);

	if (e) return EXIT_FAILURE;

	return EXIT_SUCCESS;
}
