/* AppCon Main Source
**
** Done by Stephan Fuhrmann
**
*/

#define EXEC_MINIMUM 36
#define MY_ID 3111973
/*#define DEBUG_ME*/

#include <AppCon.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dos/dos.h>
#include <dos/dosextens.h>
#include <exec/execbase.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/tasks.h>
#include <devices/console.h>
#include <devices/conunit.h>
#include <devices/keymap.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/timer.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

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

#ifdef LATTICE
int CXBRK(void) { return(0); }
int chkabort(void) { return(0); }
#endif

char __stdiowin[]="CON:10/10/500/100/AppCon";
char __stdio37[]="/CLOSE/WAIT";

long timeout=10;	/* in tenths of a second */

const char vt[]=VERSTAG;

struct Process *AppConTask=0L;
struct CommandLineInterface *AppCLI;
struct Window *AppConWindow;

extern struct ExecBase *SysBase;
extern struct IntuitionBase *IntuitionBase;
extern struct Library *WorkbenchBase;
extern struct Library *KeymapBase;
extern struct Library *DosBase;

struct MsgPort *timeport=0L;
struct MsgPort *appport=0L;
struct MsgPort *ioreply=0L;
struct IOStdReq *iorequest=0L;
struct timerequest *timerio=0L;

struct AppWindow *AppWindow;

struct AppMessage *AppMessage;

struct InputEvent *InputEvent;

ULONG WaitSignals=SIGBREAKF_CTRL_C;
ULONG ReceivedSignals;

long __OSlibversion=EXEC_MINIMUM;

char filename[FILENAME_MAX];
char dirname[FILENAME_MAX];
char pathname[FILENAME_MAX*2];

char AppConName[]="AppCon";
char Couldnt[]="AppCon: Couldn't %s %s!\n";

#define TEMPLATE	"TimeOut=Time/K/N"
enum templates {OPT_TIMEOUT,OPT_COUNT};

LONG opts[OPT_COUNT];

char HelpPage[]=
	"\nAppCon Help\n"\
	"-----------\n"\
	"© 1993 Stephan Fuhrmann\n\n"\
	"Options\n"\
	"---------\n"\
	"TIMEOUT...timeout between checks of window closing, in tenths of a second\\NUMBER\n"\
	"\nExample: AppCon TIMEOUT=10\n";

char WBHelp[]=
	"\nNote: AppCon is a Shell-only utility and can't be started\n"\
	"from the Workbench.\n";

struct InputEvent *SendEvents (struct IOStdReq *iorequest,struct InputEvent *SendUs);
void MakeActive (struct Window *ActivateMe);
struct InputEvent *BrewInputevent(char *ASCII);
void FreeIEs (struct InputEvent *firstie);
struct Window *GetConWindow (struct Process *ConProcess,struct MsgPort *MyPort);
long AntiBlocking (struct Window *MyWin);
void MegaAbort (struct IOStdReq *);
void LaunchRequest (long secs,long micros,struct timerequest *mytimerio);

int main(int argc,char *argv[])
{
	struct RDArgs *argsptr;

	fprintf (stderr,VSTRING);

	if (!argc)
	{
		fprintf (stderr,WBHelp);
		return (0);
	}

	fprintf (stderr,"PUBLIC DOMAIN written 1993 by Stephan Fuhrmann\n");

	argsptr=AllocDosObject (DOS_RDARGS,0);

	if (argsptr)
	{
		argsptr->RDA_ExtHelp=HelpPage;

		if (!ReadArgs (TEMPLATE,opts,argsptr))
		{
			PrintFault (IoErr(),NULL);
			FreeArgs (argsptr);
			FreeDosObject (DOS_RDARGS,argsptr);
			return (0);
		}
	
		if (opts[OPT_TIMEOUT])
			timeout=*(LONG *)opts[OPT_TIMEOUT];

		if ((ioreply=CreateMsgPort()) && (appport=CreateMsgPort()) && (timeport=CreateMsgPort()))
		{
			appport->mp_Node.ln_Name=AppConName;

			WaitSignals |= 1L << (ioreply->mp_SigBit);
			WaitSignals |= 1L << (timeport->mp_SigBit);
			WaitSignals |= 1L << (appport->mp_SigBit);

			if ((iorequest=CreateIORequest(ioreply,sizeof(struct IOStdReq)))
			   && (timerio=CreateIORequest(timeport,sizeof(struct timerequest))))
			{

				if (!OpenDevice ("timer.device",UNIT_VBLANK,(struct IORequest *)timerio,0))
				{
					AppConTask=(struct Process *) FindTask(NULL);

					if (!OpenDevice ("input.device",0L,(struct IORequest *)iorequest,0))
					{

						AppConWindow=GetConWindow (AppConTask,ioreply);

						if (AppConWindow)
						{
							if (AppWindow=AddAppWindowA (MY_ID,NULL,AppConWindow,appport,NULL))
							{
								ReceivedSignals=0;
	
								LaunchRequest (timeout / 10,(timeout % 10) * 100000,timerio);
	
								while (!(ReceivedSignals & SIGBREAKF_CTRL_C))
								{
									ReceivedSignals=Wait (WaitSignals);
	
									if (ReceivedSignals & (1L << (timeport->mp_SigBit)))
									{
										MegaAbort ((struct IOStdReq *)timerio);
										LaunchRequest (timeout / 10,(timeout % 10) * 100000,timerio);

										if (AntiBlocking (AppConTask->pr_ConsoleTask) <= 1)
											Signal ((struct Task *)AppConTask,SIGBREAKF_CTRL_C);
									
										ReceivedSignals &= ~ (1L << (timeport->mp_SigBit));
									}

									if (ReceivedSignals & (1L << (appport->mp_SigBit)))
									{
										ReceivedSignals &= ~ (1L << (appport->mp_SigBit));
	
										while (AppMessage = (struct AppMessage *)GetMsg (appport))
										{
											register long cnt,max;
											register struct WBArg *curarg;
											struct WBArg *argptr;
	
											if ((AppMessage->am_Type)!=MTYPE_APPWINDOW)
											{
#ifdef DEBUG_ME
												fprintf (stderr,"Unknown type (%ld)!",AppMessage->am_Type);
#endif	
												ReplyMsg((struct Message *)AppMessage);
												continue;
											}
	
											max=AppMessage->am_NumArgs;
											curarg=AppMessage->am_ArgList;
	
											if (!max)
											{
#ifdef DEBUG_ME
												fprintf (stderr,"Not enough arguments (%ld)!",max);
#endif	
												ReplyMsg((struct Message *)AppMessage);
												continue;
											}
	
											argptr=curarg;
	
											for (cnt=0;cnt<max;cnt++)
											{
												if (argptr->wa_Lock)
													NameFromLock(argptr->wa_Lock,dirname,FILENAME_MAX);
	
												strcpy (pathname,dirname);
		
												if (argptr->wa_Name)
													AddPart(pathname,argptr->wa_Name,FILENAME_MAX*2);
	
												if (cnt)
												{
													if (InputEvent=BrewInputevent (" "))
													{
														MakeActive (AppConWindow);
		
														FreeIEs(SendEvents (iorequest,InputEvent));
													}
													else
														DisplayBeep(0);
												}
	
												if (strchr(pathname,' '))
												{
													if (InputEvent=BrewInputevent ("\""))
													{
														MakeActive (AppConWindow);
	
														FreeIEs(SendEvents (iorequest,InputEvent));
													}
													else
														DisplayBeep(0);
												}
	
												if (InputEvent=BrewInputevent (pathname))
												{
													MakeActive (AppConWindow);
	
													FreeIEs (SendEvents (iorequest,InputEvent));
												}
												else
													DisplayBeep (0);
												
												if (strchr(pathname,' '))
												{
													if (InputEvent=BrewInputevent ("\""))
													{
														MakeActive (AppConWindow);
	
														FreeIEs(SendEvents (iorequest,InputEvent));
													}
													else
														DisplayBeep (0);
												}
												argptr++;
											}
											ReplyMsg((struct Message *)AppMessage);
										}
									}
								}
								RemoveAppWindow (AppWindow);
							}
							else
								fprintf (stderr,Couldnt,"add","AppWindow");
						}
						else
							fprintf (stderr,Couldnt,"find","console window!");

						MegaAbort (iorequest);

						CloseDevice ((struct IORequest *)iorequest);
					}
					else
						fprintf (stderr,Couldnt,"open","console.device");

					MegaAbort ((struct IOStdReq *)timerio);
					CloseDevice ((struct IORequest *)timerio);
				}
				else
					fprintf (stderr,Couldnt,"open","timer.device");
			}
			else
				fprintf (stderr,Couldnt,"create","IORequest");

			DeleteIORequest (timerio);
			DeleteIORequest (iorequest);
		}
		else
			fprintf (stderr,Couldnt,"create","msgport");

		DeleteMsgPort (timeport);			
		DeleteMsgPort (ioreply);
		DeleteMsgPort (appport);
	}	
	else
		fprintf (stderr,Couldnt,"allocate","ReadArgs-Object");

	FreeArgs (argsptr);

	if (argsptr)
		FreeDosObject (DOS_RDARGS,argsptr);

	return (0);
}

struct InputEvent *SendEvents (struct IOStdReq *iorequest,struct InputEvent *SendUs)
{
	struct InputEvent *ThisEvent,*NextEvent;
	
	ThisEvent=SendUs;
	
	while (ThisEvent)
	{
		iorequest->io_Command=IND_WRITEEVENT;
		iorequest->io_Length=sizeof(struct InputEvent);
		iorequest->io_Data=ThisEvent;
		NextEvent=ThisEvent->ie_NextEvent;
		ThisEvent->ie_NextEvent=0L;
		DoIO ((struct IORequest *)iorequest);
		ThisEvent->ie_NextEvent=NextEvent;		/* recover NextEvent-field for later FreeMem-stuff */
		ThisEvent=NextEvent;
	}

	return (SendUs);
}

void MakeActive (struct Window *ActivateMe)
{
	if (! ((ActivateMe->Flags) & WFLG_WINDOWACTIVE))
	{
		ActivateWindow (ActivateMe);
		while (! ((ActivateMe->Flags) & WFLG_WINDOWACTIVE))
			Delay (1);
	}
}

void FreeIEs (struct InputEvent *firstie)
{
	register struct InputEvent *nextie;

	while (firstie)
	{
		nextie=firstie->ie_NextEvent;
		FreeMem (firstie,sizeof (struct InputEvent));
		firstie=nextie;
	}
}

struct InputEvent *BrewInputevent(char *ASCII)
{
	struct InputEvent *thisie,*oldie,*grandpa,*firstie;
	register long cnt;
	long junksize;
	WORD *junkbuf;
	long rawsize;

	firstie=0;
	thisie=0;
	oldie=0;

	junksize=strlen(ASCII)*3;

	if (!(junkbuf=AllocMem(junksize*sizeof(WORD),MEMF_PUBLIC)))
		return (0);

	rawsize=MapANSI (ASCII,strlen(ASCII),(STRPTR)junkbuf,junksize,0);

	switch (rawsize)
	{
		case 0:
		case -1:
		case -2:
			FreeMem (junkbuf,junksize*sizeof(WORD));
			return (0);
	}

	for (cnt=0;cnt<rawsize;cnt++)
	{
		grandpa=oldie;
		oldie=thisie;

		thisie=AllocMem(sizeof (struct InputEvent),MEMF_CLEAR|MEMF_PUBLIC);

		if (!thisie)
		{
			FreeMem (junkbuf,junksize*sizeof(WORD));
			FreeIEs(firstie);
			return (0);
		}
		
		if (oldie)
			oldie->ie_NextEvent=thisie;
		else
			firstie=thisie;

		/* PARANOIA! */
		thisie->ie_NextEvent=0;

		thisie->ie_Class=IECLASS_RAWKEY;
		thisie->ie_SubClass=0;
		thisie->ie_Code=(*(junkbuf+cnt)>>8) & 255;
		thisie->ie_Qualifier=*(junkbuf+cnt) & 255;

		if (oldie)
		{
			thisie->ie_position.ie_dead.ie_prev1DownCode=oldie->ie_Code;
			thisie->ie_position.ie_dead.ie_prev1DownQual=oldie->ie_Qualifier;
		}

		if (grandpa)
		{
			thisie->ie_position.ie_dead.ie_prev2DownCode=grandpa->ie_Code;
			thisie->ie_position.ie_dead.ie_prev2DownQual=grandpa->ie_Qualifier;
		}
	}

	FreeMem (junkbuf,junksize*sizeof(WORD));
	return (firstie);
}

struct Window *GetConWindow (struct Process *ConProcess,struct MsgPort *MyPort)
{
	struct Window *ResultWindow;
	struct MsgPort *con;
	struct StandardPacket packet;
	struct InfoData id;

	ResultWindow=0L;

	if (ConProcess->pr_Task.tc_Node.ln_Type == NT_PROCESS)
	{
		con=ConProcess->pr_ConsoleTask;

		if (con)
		{
			packet.sp_Msg.mn_Node.ln_Name=(char *)&(packet.sp_Pkt);
			packet.sp_Pkt.dp_Link=&(packet.sp_Msg);
			packet.sp_Pkt.dp_Port=MyPort;
			packet.sp_Pkt.dp_Type=ACTION_DISK_INFO;
			packet.sp_Pkt.dp_Arg1=((ULONG) &id) >> 2;
			PutMsg (con,(struct Message *)&packet);
			WaitPort (MyPort);
			ResultWindow=(struct Window *)id.id_VolumeNode;
		}
	}

	return (ResultWindow);
}

long AntiBlocking (APTR MyConsoleTask)
{
	long winusers=0;
	long cnt,max;

	Forbid();

	max=MaxCli()+1;

	for (cnt=1;cnt < max;cnt++)
	{
		struct Process *ThisProc;

		if (ThisProc=FindCliProc (cnt))
		{
			if (ThisProc->pr_ConsoleTask == MyConsoleTask)
				winusers++;
		}
	}

	Permit();
	return (winusers);
}

void MegaAbort (struct IOStdReq *AbortMe)
{
	AbortIO ((struct IORequest *)AbortMe);
	WaitIO ((struct IORequest *)AbortMe);
	SetSignal (0L,1L << (AbortMe->io_Message.mn_ReplyPort->mp_SigBit));
}

void LaunchRequest (long secs,long micros,struct timerequest *mytimerio)
{
	timerio->tr_node.io_Command=TR_ADDREQUEST;
	timerio->tr_time.tv_secs=secs;
	timerio->tr_time.tv_micro=micros;
	SendIO ((struct IORequest *)mytimerio);
}
