/*
 * $RCSfile: os.cpp $
 *
 * $Author: marcel $
 *
 * $Revision: 1.8 $
 *
 * $Date: 1995/05/15 11:28:02 $
 *
 * $Locker: marcel $
 *
 * $State: Exp $
 *
 * Amiga version
 *
 * Copyright © 1995 Marcel Offermans
 *
 * tabsize = 5
 */

/* includes */
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <dos.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "track.h"
#include "os.h"
#include "car.h"

#define TEMPLATE				"TRACK,DRIVERS/M,CARS/N,PRACTICELAPS/N,LAPS/N,RACES/N,SURFACETYPE/N,KEEPORDER/S,NOREALTIME/S,NODISPLAY/S,NORANDOMSEED/N,NORANDOM/S,NOKEYBOARD/S,HELP/S,NOGUI/S,SCREENDEPTH/N,SCREENWIDTH/N,SCREENHEIGHT/N,SCREENMODEID/N,SCREENOVERSCANTYPE/N,SCREENNOAUTOSCROLL/S"
#define OPT_TRACK				0
#define OPT_DRIVERS				1
#define OPT_CARS				2
#define OPT_PRACTICELAPS			3
#define OPT_LAPS         		4
#define OPT_RACES        		5
#define OPT_SURFACETYPE			6
#define OPT_KEEPORDER    		7
#define OPT_NOREALTIME   		8
#define OPT_NODISPLAY    		9
#define OPT_NORANDOMSEED 		10
#define OPT_NORANDOM     		11
#define OPT_NOKEYBOARD			12
#define OPT_HELP				13
#define OPT_NOGUI				14
#define OPT_SCREENDEPTH			15
#define OPT_SCREENWIDTH			16
#define OPT_SCREENHEIGHT			17
#define OPT_SCREENMODEID			18
#define OPT_SCREENOVERSCANTYPE	19
#define OPT_SCREENNOAUTOSCROLL	20
#define OPT_COUNT				21

#define ENVBUFSIZE 1024

/* externals */
extern "C" VOID show_gui(VOID);
extern car_ID drivers[];
void print_help_file(void);
int find_name(char *);
void version_report(void);
void Randomize(long);

/* globals */
static char				version_ptr[]		= "\0$VER: RARS_Amiga 3.0 " __AMIGADATE__ ;
int						rndmiz			= 1;
int						no_waiting		= 0;
int						randomotion		= 1;
int						practice			= 0;
static long				seed				= 0L;
struct RDArgs *			args_ptr			= NULL;
struct IntuiMessage *		imsg_ptr			= NULL;
struct IntuiMessage			imsg;
LONG						opts[OPT_COUNT];
ULONG					idcmpmask;
ULONG					signals;
struct RDArgs *			rdargs_ptr		= NULL;
UBYTE					envbuf_ptr[ENVBUFSIZE];

/* filenames are case insensitive on the Amiga and therefore we need this string comparison routine */
int strcmpnocase(char *s_ptr, char *t_ptr)
{
	int i;

	for (i = 0; toupper(s_ptr[i]) == toupper(t_ptr[i]); i++)
	{
		if (s_ptr[i] == '\0')
		{
			return(0);
		}
	}
	return(toupper(s_ptr[i]) - toupper(t_ptr[i]));
}

/* initialize the random number generator based on the current time */
void randomizer(void)
{
	if (rndmiz)
	{
		Randomize(0);
	}
	else if (seed)
	{
		Randomize(seed);
	}
	return;
}

/* return a random number between 0 and limit */
double random(int limit)
{
	return(((double)rand() / (double)RAND_MAX) * (double)limit);
}

/* convert integer to ascii */
void itoa(int value, char *buffer_ptr, int base)
{
	if (base == 10)
	{
		stci_d(buffer_ptr, value);
	}
}

/* get arguments by using a standard command line template */
void get_args(int argc, char* argv[])
{
	int i, n, loop;
	car_ID temp_drv;
	char **driver_ptr_ptr;
	BOOL nogui = FALSE;

	/* set up defaults */
	strcpy(trackfile, "Trackfile.trk");
	lap_count = 4;
	car_count = 6;
	real_speed = 1;
	race_count = 2;
	keep_order = 0;
	surface = 0;

	/* get environment variables */
	if (GetVar("RARSOPTS", (STRPTR)envbuf_ptr, (long)ENVBUFSIZE, (long)0) > 0)
	{
		strcat((char *)envbuf_ptr, "\n");
		if (rdargs_ptr = (struct RDArgs *)AllocDosObjectTagList(DOS_RDARGS, NULL))
		{
			rdargs_ptr->RDA_Source.CS_Buffer = envbuf_ptr;
			rdargs_ptr->RDA_Source.CS_Length = ENVBUFSIZE;
			rdargs_ptr->RDA_Source.CS_CurChr = 0;
			rdargs_ptr->RDA_Buffer = NULL;
			rdargs_ptr->RDA_Flags = RDAF_NOPROMPT;
		}
	}

	/* get arguments */
	for (loop = 0; loop < 2; loop++)
	{
		if (loop == 0)
		{
			if (rdargs_ptr == NULL)
			{
				continue;
			}
		}

		memset(opts, 0, OPT_COUNT * sizeof(LONG));

		if (args_ptr = ReadArgs(TEMPLATE, opts, (loop == 0) ? (rdargs_ptr) : (NULL)))
		{
			if (opts[OPT_TRACK])
			{
				/* track file name */
				strcpy(trackfile, (char *)opts[OPT_TRACK]);
			}
			if (opts[OPT_DRIVERS])
			{
				/* list of drivers in the race */
				driver_ptr_ptr = (char **)opts[OPT_DRIVERS];
				for (n = 0; n < MAXCARS; n++)
				{
					if (driver_ptr_ptr[n] == NULL)
					{
						break;
					}
					if ((i = find_name(driver_ptr_ptr[n])) < 0)
					{
						break;
					}
					temp_drv = drivers[n];
					drivers[n] = drivers[i];
					drivers[i] = temp_drv;
				}
				/* if any drivers were found, set the number of cars to the number of drivers */
				if (n > 0)
				{
					car_count = n;
				}
			}
			if (opts[OPT_LAPS])
			{
				/* number of laps to go */
				lap_count = (int)(*((LONG *)opts[OPT_LAPS]));
	
				/* check bounds of user input */
				if (lap_count <= 0)
				{
					lap_count = 1;
				}
			}
			if (opts[OPT_PRACTICELAPS])
			{
				/* number of laps to go */
				practice = (int)(*((LONG *)opts[OPT_PRACTICELAPS]));
	
				/* check bounds of user input */
				if (practice < 0)
				{
					practice = 0;
				}
			}
			if (opts[OPT_SURFACETYPE])
			{
				/* number of laps to go */
				surface = (int)(*((LONG *)opts[OPT_SURFACETYPE]));
	
				/* check bounds of user input */
				if (surface < 0)
				{
					surface = 0;
				}
			}
			if (opts[OPT_CARS])
			{
				/* number of cars in the race */
				car_count = (int)(*((LONG *)opts[OPT_CARS]));
	
				/* check bounds of user input */
				if (car_count < 0)
				{
					/* zero cars is legal, and only shows you the track */
					car_count = 0;
				}
				else if (car_count > MAXCARS)
				{
					car_count = MAXCARS;
				}
			}
			if (opts[OPT_RACES])
			{
				/* number of races to go */
				race_count = (int)(*((LONG *)opts[OPT_RACES]));
	
				/* check bounds of user input */
				if (race_count <= 0)
				{
					race_count = 1;
				}
			}
			if (opts[OPT_NOREALTIME])
			{
				/* run as fast as possible instead of real-time */
				real_speed = 0;
			}
			if (opts[OPT_NORANDOMSEED])
			{
				/* don't set a new random seed every time */
				rndmiz = 0;
	
				/* set the seed */
				seed = (int)(*((LONG *)opts[OPT_NORANDOMSEED]));
			}
			if (opts[OPT_NODISPLAY])
			{
				/* don't use a graphics display */
				no_display = 1;
			}
			if (opts[OPT_KEEPORDER])
			{
				/* keep the starting order */
				keep_order = 1;
			}
			if (opts[OPT_NORANDOM])
			{
				/* don't randomize at all */
				rndmiz = 0;
				randomotion = 0;
			}
			if (opts[OPT_NOKEYBOARD])
			{
				/* don't process user keystrokes */
				no_waiting = 1;
			}
			if (opts[OPT_HELP])
			{
				/* show help and quit */
				print_help_file();
				FreeArgs(args_ptr);
				exit(0);
			}
			if (opts[OPT_NOGUI])
			{
				/* don't display the GUI */
				nogui = TRUE;
			}
			if (opts[OPT_SCREENDEPTH])
			{
				scrdepth = (ULONG)(*((LONG *)opts[OPT_SCREENDEPTH]));
			}
			if (opts[OPT_SCREENWIDTH])
			{
				scrwidth = (ULONG)(*((LONG *)opts[OPT_SCREENWIDTH]));
			}
			if (opts[OPT_SCREENHEIGHT])
			{
				scrheight = (ULONG)(*((LONG *)opts[OPT_SCREENHEIGHT]));
			}
			if (opts[OPT_SCREENMODEID])
			{
				scrid = (ULONG)(*((LONG *)opts[OPT_SCREENMODEID]));
			}
			if (opts[OPT_SCREENOVERSCANTYPE])
			{
				scroscantype = (ULONG)(*((LONG *)opts[OPT_SCREENOVERSCANTYPE]));
			}
			if (opts[OPT_SCREENNOAUTOSCROLL])
			{
				scrautoscroll = FALSE;
			}
			FreeArgs(args_ptr);
		}
	}

	if (rdargs_ptr)
	{
		FreeDosObject(DOS_RDARGS, (void *)rdargs_ptr);
		rdargs_ptr = NULL;
	}

	/* show the GUI unless the user turned it off */
	if (nogui == FALSE)
	{
		show_gui();
	}
}

/* blocks until one character is read from the keyboard */
int get_ch(void)
{
	if (!available)
	{
		/* if there's no graphics display, we don't handle the keyboard */
		return(0);
	}

	/* wait for a message if we don't have a cached message yet */
	while (imsg_ptr == NULL)
	{
		/* if we don't want to wait for a key, then fall through */
		if (!(no_waiting))
		{
			signals = Wait(idcmpmask);
		}
		else
		{
			signals = idcmpmask;
		}
		if (signals & idcmpmask)
		{
			imsg_ptr = (struct IntuiMessage *)GetMsg(window_ptr->UserPort);
			if (imsg_ptr)
			{
				CopyMem((char *)imsg_ptr, (char *)&imsg, (long)sizeof(struct IntuiMessage));
				ReplyMsg((struct Message *)imsg_ptr);
			}
			else if (no_waiting)
			{
				/* return a code if we don't want to wait */
				imsg.Code = ' ';
			}
		}
	}

	/* reset the pointer because at this point the message is processed */
	imsg_ptr = NULL;

	/* return the key code */
	return(imsg.Code);
}

/* this routine returns non-zero if there's input available for get_ch() and 0 if not */
int kb_hit(void)
{
	if (!available)
	{
		/* if there's no graphics display, we don't handle the keyboard */
		return(0);
	}

	imsg_ptr = (struct IntuiMessage *)GetMsg(window_ptr->UserPort);
	if (imsg_ptr)
	{
		/* we received a message, so now we have to cache it until get_ch() is called */
		CopyMem((char *)imsg_ptr, (char *)&imsg, (long)sizeof(struct IntuiMessage));
		ReplyMsg((struct Message *)imsg_ptr);
		return(1);
	}
	else
	{
		return(0);
	}
}

/* busy wait for a tick to go by */
void one_tick(int initialize = 0)
{
	static unsigned int pre_clock[2];
	static unsigned int cur_clock[2];

	if (initialize)
	{
		timer(pre_clock);
	}
	else
	{
		do
		{
			timer(cur_clock);
		}
		while (cur_clock[1] + 1000000 * (cur_clock[0] - pre_clock[0]) < pre_clock[1] + (int)(delta_time * 1000000));

		pre_clock[0] = cur_clock[0];
		pre_clock[1] = cur_clock[1];
	}
}

/* return the amount of free RAM in K */
int RAM_query(void)
{
	return(AvailMem(MEMF_ANY) >> 10);
}

/* return a random number based on the clock */
long pick_random(void)  
{
	unsigned int clock[2];

	timer(clock);
	return((long)clock[1]);
}
