/*
**	clickstart.c - enable/disable features of AmigaDOS
**	Copyright © 1994-95 Michael Letowski
*/

#define __USE_SYSBASE
#define ECS_SPECIFIC														/* Enable hardware */

#include <exec/types.h>
#include <exec/execbase.h>
#include <dos/rdargs.h>
#include <dos/var.h>
#include <devices/trackdisk.h>
#include <graphics/gfxbase.h>
#include <hardware/custom.h>
#include <support/types.h>
#include <support/exec.h>
#include <support/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include "clickstart.rev.h"


#define DOS_NAME		"dos.library"
#define DOS_VERN		37L
#define GFX_NAME		"graphics.library"
#define GFX_VERN		39L
#define INT_NAME		"intuition.library"
#define INT_VERN		0L
#define TRACK_NAME	"trackdisk.device"

/* Args template */
#define TEMPLATE		"CLICK/S,NOCLICK/S,STAR/S,NOSTAR/S,"\
										"PIPE/K,NOPIPE/S,MULTI/K,NOMULTI/S,"\
										"BORBLANK/S,NOBORBLANK/S,BORSPRITES/S,NOBORSPRITES/S,"\
										"ALERT/K/N,RETRY/K/N,QUANTUM/K/N,QUIET/S"

/* Info message */
#define MSG_STATUS	"System state:\n"\
										"\tClick is %s\n"\
										"\tStar is %s\n"\
										"\tPipe char is %s\n"\
										"\tMulti command char is %s\n"\
										"\tBorder blank is %s\n"\
										"\tBorder sprites are %s\n"\
										"\tAlert lasts %ld frame%s\n"\
										"\tNumber of retries is %ld\n"\
										"\tDispatch quantum is %ld frame%s\n"

/* Pipe enable/disable */
#define PIPE_VAR_NAME		"_pchar"

/* Multi command enable/disable */
#define MULTI_VAR_NAME	"_mchar"

#define VAR_LEN					1
#define VAR_FLAGS				(LV_VAR | GVF_LOCAL_ONLY)
#define VAR_SIZE				(VAR_LEN+3)

/* Default pipe and multi characters */
#define DEF_PIPE				"|"
#define DEF_MULTI				"\\"

/* On/off texts */
#define ON							"ON"
#define OFF							"OFF"
#define UNKNOWN					"?"

/* Ranges */
#define MIN_RETRY				0
#define MAX_RETRY				255
#define MIN_ALERT				0
#define MAX_ALERT				0x7FFFFFFF
#define MIN_QUANT				1
#define MAX_QUANT				65535

#define PStr(arg,def)		(*(arg)=='\0' ? (def) : (arg))

STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);

struct Options
{
	LONG   opt_Click;															/* C=CLICK/S */
	LONG   opt_NoClick;														/* NC=NOCLICK/S */
	LONG   opt_Star;															/* S=STAR/S */
	LONG   opt_NoStar;														/* NS=NOSTAR/S */
	STRPTR opt_Pipe;															/* P=PIPE/K */
	LONG   opt_NoPipe;														/* NP=NOPIPE/S */
	STRPTR opt_Multi;															/* M=MULTI/S */
	LONG   opt_NoMulti;														/* NM=NOMULTI/S */
	LONG   opt_Blank;															/* BB=BORBLANK/S */
	LONG   opt_NoBlank;														/* NBB=NOBORBLANK/S */
	LONG   opt_Sprites;														/* BS=BORSPRITES */
	LONG   opt_NoSprites;													/* NBS=NOBORSPRITES */
	LONG  *opt_Alert;															/* A=ALERT/K/N */
	LONG  *opt_Retry;															/* R=RETRY/K/N */
	LONG  *opt_Quant;															/* Q=QUANTUM/K/N */
	LONG   opt_Quiet;															/* QUIET/S */
};	/* Options */

struct States
{
	STRPTR state_Click;
	STRPTR state_Star;
	STRPTR state_Pipe;
	STRPTR state_Multi;
	STRPTR state_Blank;
	STRPTR state_Sprites;
	LONG   state_Alert;
	STRPTR state_AlertS;
	LONG   state_Retry;
	LONG   state_Quant;
	STRPTR state_QuantS;
};	/* States */

STATIC VOID HandleTrackDisk(struct Options *opts, struct States *stat, LONG *error);
STATIC VOID HandleVar(STRPTR varName, STRPTR varVal, STRPTR defVal, LONG off,
											LONG *error, STRPTR *result, struct DosLibrary *DOSBase);
STATIC VOID HandleBorder(struct Options *opts, struct States *stat, LONG *error,
													struct DosLibrary *DOSBase);

LONG ClickStart(VOID)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct DosLibrary *DOSBase;

	CHAR PipeVal[VAR_SIZE],MultiVal[VAR_SIZE];
	struct Options Opts;
	struct States Stat;
	struct RDArgs *Args;
	LONG *DOSFlags,RC=RETURN_FAIL;

	/* Open DOS */
	unless(DOSBase=(struct DosLibrary *)OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY), NO_DOS);

	/* Read arguments */
	clear(&Opts);															/* Clear out buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_ARGS);
	/* Check args */
	if(Opts.opt_Click && Opts.opt_NoClick || Opts.opt_Star && Opts.opt_NoStar ||
			Opts.opt_Pipe && Opts.opt_NoPipe || Opts.opt_Multi && Opts.opt_NoMulti ||
      Opts.opt_Blank && Opts.opt_NoBlank || Opts.opt_Sprites && Opts.opt_NoSprites)
		throw2(CauseIoErr(ERROR_TOO_MANY_ARGS,PROG_NAME),	BAD_ARGS);
	/* Make values legal*/
	if(Opts.opt_Alert)	*Opts.opt_Alert=clamp(*Opts.opt_Alert,MIN_ALERT,MAX_ALERT);
	if(Opts.opt_Retry)	*Opts.opt_Retry=clamp(*Opts.opt_Retry,MIN_RETRY,MAX_RETRY);
	if(Opts.opt_Quant)	*Opts.opt_Quant=clamp(*Opts.opt_Quant,MIN_QUANT,MAX_QUANT);

	RC=RETURN_ERROR;													/* Working a bit */

	/* Handle clicking */
	HandleTrackDisk(&Opts,&Stat,&RC);

	/* Wildstar */
	DOSFlags=&DOSBase->dl_Root->rn_Flags;
	if(Opts.opt_Star)		fset(*DOSFlags,RNF_WILDSTAR);
	if(Opts.opt_NoStar)	fclr(*DOSFlags,RNF_WILDSTAR);
	Stat.state_Star=ftst(*DOSFlags,RNF_WILDSTAR) ? ON : OFF;

	/* Piping */
	Stat.state_Pipe=PipeVal;
	HandleVar(PIPE_VAR_NAME,Opts.opt_Pipe,DEF_PIPE,
						Opts.opt_NoPipe,&RC,&Stat.state_Pipe,DOSBase);

	/* Multi command execution */
	Stat.state_Multi=MultiVal;
	HandleVar(MULTI_VAR_NAME,Opts.opt_Multi,DEF_MULTI,
						Opts.opt_NoMulti,&RC,&Stat.state_Multi,DOSBase);

	/* Border stuff */
	HandleBorder(&Opts,&Stat,&RC,DOSBase);

	/* Alerts timing */
	if(Opts.opt_Alert)	SysBase->LastAlert[3]=*Opts.opt_Alert;
	Stat.state_Alert=SysBase->LastAlert[3];
	Stat.state_AlertS=(Stat.state_Alert==1) ? NULL : "s";

	/* Quantum */
	if(Opts.opt_Quant)	SysBase->Quantum=*Opts.opt_Quant;
	Stat.state_Quant=SysBase->Quantum;
	Stat.state_QuantS=(Stat.state_Quant==1) ? NULL : "s";

	unless(Opts.opt_Quiet)
		VPrintf(MSG_STATUS,(LONG *)&Stat);

	catch(BAD_ARGS,	);
	catch(NO_ARGS,	FreeArgs(Args));
	catch(NO_DOS,		CloseLibrary((struct Library *)DOSBase));
	return(RC);
}	/* ClickStart */

STATIC VOID HandleTrackDisk(struct Options *opts, struct States *stat, LONG *error)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);

	struct MsgPort *Port;
	struct IOExtTD *IO;
	struct TDU_PublicUnit *TDUUnit;
	ULONG Unit;

	stat->state_Click=UNKNOWN;										/* We don't know yet */

	try(Port=CreateMsgPort(),															NO_PORT);
	try(IO=CreateIORequest(Port,sizeof(struct IOExtTD)),	NO_IO)

	*error=RETURN_OK;													/* Everything OK so far */
	for(Unit=0; Unit<4; Unit++)								/* For each drive */
		if(OpenDevice(TRACK_NAME,Unit,(struct IORequest *)IO,0)==0)
		{
			TDUUnit=(struct TDU_PublicUnit *)IO->iotd_Req.io_Unit;
			/* Disable/enable clicking */
			if(opts->opt_Click)		fclr(TDUUnit->tdu_PubFlags,TDPF_NOCLICK);
			if(opts->opt_NoClick)	fset(TDUUnit->tdu_PubFlags,TDPF_NOCLICK);
			stat->state_Click=ftst(TDUUnit->tdu_PubFlags,TDPF_NOCLICK) ? OFF : ON;

			/* Change retry count */
			if(opts->opt_Retry)		TDUUnit->tdu_RetryCnt=*opts->opt_Retry;
			stat->state_Retry=TDUUnit->tdu_RetryCnt;

			/* Clean up */
			CloseDevice((struct IORequest *)IO);
		}

	catch(NO_PORT,	DeleteMsgPort(Port));
	catch(NO_IO,		DeleteIORequest(IO));
}	/* HandleTrackDisk */

STATIC VOID HandleVar(STRPTR varName, STRPTR varVal, STRPTR defVal, LONG off,
											LONG *error, STRPTR *result, struct DosLibrary *DOSBase)
{
	STRPTR State=*result;
	struct LocalVar *Var;

	if(varVal)
		unless(SetVar(varName,PStr(varVal,defVal),VAR_LEN,VAR_FLAGS))
		{
			PrintFault(IoErr(),PROG_NAME);
			*error=RETURN_ERROR;
		}
	if(off)
		unless(DeleteVar(varName,VAR_FLAGS))
		{
			PrintFault(IoErr(),PROG_NAME);
			*error=RETURN_ERROR;
		}
	if(Var=FindVar(varName,VAR_FLAGS))						/* Could find var */
	{
		State[0]='\'';															/* Store its contents */
		State[1]=*(Var->lv_Value);
		State[2]='\'';
		State[3]='\0';
	}
	else																					/* Var nor found */
		*result=OFF;																/* So it's off */
}	/* HandleVar */

STATIC VOID HandleBorder(struct Options *opts, struct States *stat, LONG *error,
													struct DosLibrary *DOSBase)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct GfxBase *GfxBase;
	struct IntuitionBase *IntuitionBase;

	UBYTE BP3Bits;

	stat->state_Sprites=UNKNOWN;
	stat->state_Blank=UNKNOWN;

	if(GfxBase=(struct GfxBase *)OpenLibrary(GFX_NAME,GFX_VERN))
	{
		BP3Bits=GfxBase->BP3Bits;										/* Remember flags */

		/* Sprites on border */
		if(opts->opt_Sprites)			fset(GfxBase->BP3Bits,BPLCON3_EXTBLKZD);
		if(opts->opt_NoSprites)		fclr(GfxBase->BP3Bits,BPLCON3_EXTBLKZD);
		stat->state_Sprites=ftst(GfxBase->BP3Bits,BPLCON3_EXTBLKZD) ? ON : OFF;

		/* Blank border */
		if(opts->opt_Blank)				fset(GfxBase->BP3Bits,BPLCON3_BRDNBLNK);
		if(opts->opt_NoBlank)			fclr(GfxBase->BP3Bits,BPLCON3_BRDNBLNK);
		stat->state_Blank=ftst(GfxBase->BP3Bits,BPLCON3_BRDNBLNK) ? ON : OFF;

		if(BP3Bits!=GfxBase->BP3Bits)								/* Changed something */
			if(IntuitionBase=(struct IntuitionBase *)OpenLibrary(INT_NAME,INT_VERN))
			{
				RemakeDisplay();												/* Make changes visible */
				CloseLibrary((struct Library *)IntuitionBase);
			}
			else																			/* Unable to open intuition */
				*error=RETURN_WARN;											/* Just warning */

		CloseLibrary((struct Library *)GfxBase);		/* Close graphics */
	}
	else																					/* No graphics */
	{
		*error=RETURN_ERROR;												/* An error */
		CauseIoErr(ERROR_INVALID_RESIDENT_LIBRARY,PROG_NAME);
	}
}	/* HandleBorder */
