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

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

#include <AppCon.h>
#include <dos.h>
//#include <stdio.h>
//#include <stdlib.h>

#define FILENAME_MAX 128

#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>

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

struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase;
struct Library *WorkbenchBase;
struct Library *KeymapBase;
struct DosLibrary *DOSBase;
struct Library *UtilityBase;

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

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

const char vt[]=VERSTAG;

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

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;

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

char AppConName[]="AppCon";

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

LONG opts[OPT_COUNT];

char HelpPage[]=
	"\nAppCon Help\n"\
	"-----------\n"\
	"© 1994 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";

int breakother (struct Process *MyProc);
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);

__saveds int main(int argc,char *argv)
{
	struct RDArgs *argsptr;
	APTR oldname;

	SysBase=*(struct ExecBase **)4;

	AppConTask=(struct Process *) FindTask(NULL);

	oldname=AppConTask->pr_Task.tc_Node.ln_Name;
	AppConTask->pr_Task.tc_Node.ln_Name=AppConName;

	if (!argc)
		return (0);

	if (DOSBase=(struct DosLibrary *)OpenLibrary ("dos.library",37))
	{
		PutStr (VSTRING);
		PutStr ("PUBLIC DOMAIN written 1993/94 by Stephan Fuhrmann\n");

		if (IntuitionBase=(struct IntuitionBase *)OpenLibrary ("intuition.library",37))
		{
			if (WorkbenchBase=OpenLibrary ("workbench.library",37))
			{
				if (KeymapBase=OpenLibrary ("keymap.library",37))
				{
					if (UtilityBase=OpenLibrary ("utility.library",37))
					{
						if (breakother (AppConTask))
						{
							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))
										{
							
											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)))
															{
																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);
															}
		
															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
																		VPrintf ("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
																		VPrintf   ("Not enough arguments (%ld)!",&max);
#endif	
																		ReplyMsg((struct Message *)AppMessage);
																		continue;
																	}
	
																	argptr=curarg;
		
																	for (cnt=0;cnt<max;cnt++)
																	{
																		int hasspace;
																		int plusend;
	
																		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);
																		}
			
																		hasspace=(int)strchr(pathname,' ');
																		plusend=*(pathname+strlen(pathname)-1)=='+';
	
																		if (hasspace || plusend)
																		{
																			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 (hasspace || plusend)
																		{
																			if (InputEvent=BrewInputevent ("\""))
																			{
																				MakeActive (AppConWindow);
			
																				FreeIEs(SendEvents (iorequest,InputEvent));
																			}
																			else
																				DisplayBeep (0);
																		}
																		argptr++;
																	}
	
																	ReplyMsg((struct Message *)AppMessage);
																}
															}
														}
														RemoveAppWindow (AppWindow);
													}
													else
														PutStr ("Couldn't add AppWindow\n");
												}
												else
													PutStr ("Couldn't find console window!\n");

												MegaAbort (iorequest);
	
												CloseDevice ((struct IORequest *)iorequest);
											}
											else
												PutStr ("Couldn't open console.device");
	
											MegaAbort ((struct IOStdReq *)timerio);
											CloseDevice ((struct IORequest *)timerio);
										}
										else
											PutStr ("Couldn't open timer.device\n");
									}
									else
										PutStr ("Couldn't create IORequest\n");
		
									DeleteIORequest (timerio);
									DeleteIORequest (iorequest);
								}
								else
									PutStr ("Couldn't create msgport\n");

								DeleteMsgPort (timeport);			
								DeleteMsgPort (ioreply);
								DeleteMsgPort (appport);
							}	
							else
								PutStr ("Couldn't allocate ReadArgs-Object\n");

							FreeArgs (argsptr);

							if (argsptr)
								FreeDosObject (DOS_RDARGS,argsptr);
						}
						else
							PutStr ("Removed existing AppCon-Task.\n");

						CloseLibrary (UtilityBase);
					}
					CloseLibrary (KeymapBase);
				}
				CloseLibrary (WorkbenchBase);
			}
			CloseLibrary ((struct Library *)IntuitionBase);
		}

		CloseLibrary ((struct Library *)DOSBase);
	}

	AppConTask->pr_Task.tc_Node.ln_Name=oldname;

	return (0);
}

int breakother (struct Process *MyProc)
{
	struct List *MyList;
	struct Process *ProcPtr;
	APTR MyConTask;
	UWORD cnt;
	int result=1;

	/* critical sections start */
	Forbid();

	MyConTask=MyProc->pr_ConsoleTask;

	for (cnt=0;cnt<2;cnt++)
	{
		MyList=cnt ? &SysBase->TaskReady : &SysBase->TaskWait;

		for (ProcPtr=(struct Process *)MyList->lh_Head ;
				ProcPtr->pr_Task.tc_Node.ln_Succ!=MyList->lh_Tail ;
				ProcPtr=(struct Process *)ProcPtr->pr_Task.tc_Node.ln_Succ)
		{
			if (ProcPtr->pr_Task.tc_Node.ln_Type==NT_PROCESS)
			{
				if ((ProcPtr->pr_ConsoleTask==MyConTask) && (ProcPtr!=MyProc))
				{
					if (!strcmp(ProcPtr->pr_Task.tc_Node.ln_Name,AppConName))
					{
						Signal ((struct Task *)ProcPtr,SIGBREAKF_CTRL_C);
						result=0;
					}
				}
			}
		}
	}

	Permit();
	/* critical section end */

	return (result);
}

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;
		__emit (0x48e7);
		__emit (0xfffe);
		DoIO ((struct IORequest *)iorequest);
		__emit (0x4cdf);
		__emit (0x7fff);
		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=AllocVec(junksize*sizeof(WORD),MEMF_CLEAR|MEMF_PUBLIC)))
		return (0);

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

	switch (rawsize)
	{
		case 0:
		case -1:
		case -2:
			FreeVec (junkbuf);
			return (0);
	}

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

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

		if (!thisie)
		{
			FreeVec (junkbuf);
			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;
		}
	}

	FreeVec (junkbuf);
	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);
}
