/********************************************************
**                                                     **
**      $VER: Events.c started on 30.08.2001           **
**                                                     **
**      Handle user events coming from all sides.      **
**                                                     **
**      Free software written by T.Pierron             **
**                                                     **
********************************************************/

#include <intuition/intuition.h>				/* Std datatypes */
#include <intuition/intuitionbase.h>		/* To access ActiveScreen */
#include <intuition/screens.h>				/* Screen information */
#include <libraries/gadtools.h>				/* NewMenus and NewGadget */
#include <workbench/workbench.h>				/* struct AppMessage */
#include <workbench/startup.h>				/* struct WBArg */
#include <dos/dos.h>								/* CTRL/C signal */
#include <dos/dosextens.h>						/* struct Process */
#include <dos/dostags.h>						/* Tags for SystemTagList() */

#include "Execute.h"

/* External variables */
extern struct IntuitionBase *IntuitionBase;
extern struct Window *window;
extern struct Screen *screen;
extern struct Menu   *menu;
extern struct Gadget *strgad;
extern UBYTE  Title[128];
extern ReviewBuf rb;

struct MsgPort *broker_mp = NULL;			/* Public message port */
struct IntuiMessage msgbuf, *msg;
STRPTR StrBuf;										/* Shortcut to string gadget's buffer */
struct {												/* Output window parameters */
	WORD   position[4];
	STRPTR title;
}	Outwin = { 0x7fff, 0x7fff, -100, 200, NULL };

ULONG cxsigflag=0;								/* Signal bits of the public port */
UBYTE PortName[] = APPNAME;					/* To quit if task already running */
UBYTE Template[] = TEMPLATE;					/* ReadArgs() template */
UBYTE AutoSave = FALSE;							/* Update shell-history automaticly */
UBYTE ShowGUI  = TRUE;							/* TRUE if GUI is poped up */

/*** Change current working directory ***/
BOOL change_cwd( STRPTR wd, UBYTE set )
{
	static APTR oldcwd = NULL;

	if(set) {
		APTR cwd;
		/* Disable system requesters (disk full, insert disk...) */
		struct Process *my_proc = (struct Process *) FindTask(NULL);

		my_proc->pr_WindowPtr = (APTR) -1;

		if(NULL == (cwd = (APTR) Lock(wd, SHARED_LOCK))) {
			/* Can't get lock: avert user something go wrong */
			if(set == 1)
				Fault(IoErr(), wd, Title, sizeof(Title)),
				AvertUser( Title );
			return FALSE;
		}
		/* Be sure object is a directory */
		if(set == 2) {
			struct FileInfoBlock * fib;
			LONG                   Type;
			if( fib = (void *) AllocVec(sizeof(*fib), 0) )
			{
				Examine(cwd, fib);
				Type = fib->fib_DirEntryType;
				FreeVec( fib );
			}
			/* This is a file! */
			if(Type < 0) { UnLock(cwd); return FALSE; }
		}
		cwd = (APTR) CurrentDir( cwd );
		if(oldcwd) UnLock( cwd );
		else       oldcwd = cwd;

		my_proc->pr_WindowPtr = (APTR)window;
		return TRUE;
	}
	else /* Must reset original working directory! */
	{
		if(oldcwd != NULL)
			UnLock( CurrentDir(oldcwd) );
	}
}

/*** Initialize the message port of the commodity ***/
BOOL init_args(int argc, char *argv[])
{
	if(broker_mp = (void *) CreateMsgPort())
	{
		extern WORD      Geometry[];
		struct MsgPort * reply;
		struct RdArgs  * ra;
		ULONG            Options[9];

		cxsigflag = 1 << broker_mp->mp_SigBit;

		/* Check if public port already exists */
		if(reply = (void *) FindPort(PortName))
		{
			struct IntuiMessage *cmd;
			if( cmd = (void *) CreateIORequest(broker_mp, sizeof(*cmd)) )
			{
				/* Yes! send a quit msg to the app */
				cmd->Class = IDCMP_CLOSEWINDOW;
				PutMsg(reply, cmd);
				Wait(cxsigflag | SIGBREAKF_CTRL_C );
				/* Unqueue message */
				GetMsg( broker_mp );
				DeleteExtIO(cmd, sizeof(*cmd));
			}
			DeleteMsgPort( broker_mp ); broker_mp = NULL;
			return FALSE;
		}
		/* Set this port public */
		broker_mp->mp_Node.ln_Name = PortName;
		broker_mp->mp_Node.ln_Pri  = 0;

		AddPort( broker_mp );
		memset(Options, 0, sizeof(Options));

		/* Analyse command line arguments */
		ra = (argc <= 1 ? NULL : (void *) ReadArgs(Template, Options, NULL));

		/* Init review buffer & command to execute */
		load_prefs( &rb, Options[3] );
		if( Options[8] ) AddToBuffer( &rb, (STRPTR) Options[8] ), ShowGUI = FALSE;
		Parse_geometry(Geometry,Options[0] ? (STRPTR)Options[0] : (STRPTR)",,400");
		Parse_geometry(&Outwin, Options[1] ? (STRPTR)Options[1] : (STRPTR)",100,-100,200");
		if( Options[2] ) AutoSave = TRUE;
		if( Options[4] ) change_cwd((STRPTR)Options[4], 1);
		if( Options[7] ) ShowGUI = TRUE;
		ToggleFlags(NM_INFO_INDEX, Options[5] ? CHECKED : 0);
		ToggleFlags(NM_OUT_INDEX,  Options[6] ? CHECKED : 0);
		if(ra) FreeArgs(ra);
		return TRUE;
	}
	PrintFault(IoErr(), PortName);
	return FALSE;
}

/** Remove public port **/
void free_msgport( void )
{
	if( broker_mp )
	{
		RemPort( broker_mp );
		DeleteMsgPort( broker_mp );
		broker_mp = NULL;
	}
}

/** SPrintf like routine **/
static STRPTR savea3;
#ifdef	__GNUC__					/* Register based-argument passing with gcc */
static void PutChProc( void )
{
	register UBYTE data __asm("d0");

#else									/* Same proc with SAS/C */
static void __asm PutChProc(register __d0 UBYTE data, register __a3 STRPTR out)
{
#endif
	/* Can't use a3 ; compiler will restore register content on exit */
	if( savea3 < Title + sizeof(Title) - 1 )
		*savea3++ = data;
	else *savea3 = 0;
}

/*** Execute a command string ***/
void ExecuteCommand( STRPTR command )
{
	static UBYTE Out[]="CON:%d/%d/%d/%d/%s/AUTO/CLOSE/WAIT/SCREEN*";
	/* Note: AUTO   = window will be open only if data is written in.
	**       CLOSE  = window will have a close gadget
	**       WAIT   = once window is opened, it will wait user close it
	**       SCREEN = open window on front-most public screen */
	static ULONG SysTags[] = {
		SYS_Output, NULL,
		SYS_Input,  NULL,
		SYS_Asynch, TRUE,
		TAG_END
	};
	APTR fh = NULL;

	/* Add command to review buffer */
	AddToBuffer(&rb, command);

	/* Special feature: if the command is a directory, chdir() here */
	if( change_cwd( Trim(command), 2 ) == FALSE )
	{
		if( window != NULL )
		{
			setup_appwindow(FALSE);
			ClearMenuStrip(window);
			CloseWindow(window); window = NULL;
		}
		free_msgport();
		if( AutoSave ) save_prefs( &rb );

		/* Get output filehandle */
		if( OutFlags & CHECKED)
		{
			/* User want to see output stream */
			#define	pos	(Outwin.position)

			pos[0] = Adjust_pos(pos[0], screen->Width,
			pos[2] = Adjust_pos(pos[2], screen->Width, 0));
			pos[1] = Adjust_pos(pos[1], screen->Height,
			pos[3] = Adjust_pos(pos[3], screen->Height, 0));
			Outwin.title = GadTxt[4]; savea3 = Title;
			RawDoFmt(Out, &Outwin, PutChProc, 0);

			fh = (APTR) Open(Title, MODE_OLDFILE);
		} else
			fh = (APTR) Open("NIL:", MODE_OLDFILE);

		/* Setting the Input filehandle will cause the shell to use **
		** the same one for the output. We don't have to close it,  **
		** since in Asynch mode it will be done automatically.      */
		SysTags[3] = (ULONG) fh;
		SystemTagList(command, SysTags);

		cleanup(RETURN_OK);
	}
	else /* User gave a directory name */
	{
		sti(strgad)->BufferPos = sti(strgad)->DispPos = 0; StrBuf[0] = 0;
		RefreshGList  (strgad, window, NULL, 1);
		ActivateGadget(strgad, window, NULL);
		setup_title( TRUE );
	}
}

/*** Handle gadget messages ***/
BOOL Handle_gadget( LONG GadgetID )
{
	switch( GadgetID )
	{
		case 1:
			switch( msgbuf.Code )
			{
				case CANCEL_OP:   return TRUE;
				case EXPAND_PAT:
				case EXPAND_DIR:
				case EXPAND_FILE:
					ExpandWild     (sti(strgad), msgbuf.Code);
					RefreshGList   (strgad, window, NULL, 1);
					ActivateGadget (strgad, window, NULL);
					return FALSE;
				case INSERT_PATH:
					choose_path    (sti(strgad));
					RefreshGList   (strgad, window, NULL, 1);
					ActivateGadget (strgad, window, NULL);
					return FALSE;
				case COMPLETE_FILE:
				case COMPLETE_DIR:
				case COMPLETE_OBJ:
					CompleteFilename (sti(strgad), msgbuf.Code);
					RefreshGList     (strgad, window, NULL, 1);
					ActivateGadget   (strgad, window, NULL);
					return FALSE;
				case SAVE_PREFS:  AutoSave = !AutoSave;
			}
		case 2: if(*StrBuf) { ExecuteCommand( StrBuf ); return FALSE; }
		case 3: return TRUE;
	}
	return FALSE;
}

/*** Processes menus events ***/
BOOL Handle_menu( struct MenuItem * item )
{
	switch( (ULONG) GTMENUITEM_USERDATA(item) )
	{
		case 101: msgbuf.Code = COMPLETE_OBJ; goto case_upd;
		case 102: msgbuf.Code = EXPAND_PAT;   goto case_upd;
		case 103: msgbuf.Code = EXPAND_FILE;  goto case_upd;
		case 104: msgbuf.Code = EXPAND_DIR;   goto case_upd;
		case 105: msgbuf.Code = INSERT_PATH;
		case_upd: return Handle_gadget( 1 );
		case 106: InfoFlags = item->Flags; break;
		case 107: ShowOut( item->Flags ); break;
		case 108: return TRUE;
	}
	return FALSE;
}

/*** Handle msg coming from public msg port ***/
BOOL Handle_port( void )
{
	BOOL ret = FALSE;
	while( msg = (void *) GetMsg( broker_mp ) )
	{
		if(msg->Class == IDCMP_CLOSEWINDOW) ret = TRUE;
		ReplyMsg( msg );
	}
	return ret;
}

BOOL Handle_kbd( UBYTE Char )
{
	switch( Char )
	{
		case 'C': case 'c': ActivateGadget(strgad, window, NULL); break;
		case 'a': case 27: case 'q': case 'Q': case 'A': return TRUE;
		case 'e': case 'E': case '\n':
			if(*StrBuf) { ExecuteCommand( StrBuf ); return FALSE; }
			return TRUE;
	}
	return FALSE;
}

/*** Handle messages coming from workbench ***/
void Handle_wbapp( void )
{
	extern UBYTE       RemPartial;
	extern void       *wbport;
	struct AppMessage *amsg;
	struct WBArg      *argptr;
	UWORD              i;
	UBYTE              Path[128];

	RemPartial = FALSE;
	while (amsg = (struct AppMessage *) GetMsg(wbport))
	{
		argptr = amsg->am_ArgList;
		for (i = amsg->am_NumArgs; i; i--)
		{
			NameFromLock(argptr->wa_Lock, Path, sizeof(Path));
			if( !AddPath(sti(strgad), Path, *argptr->wa_Name ? argptr->wa_Name : NULL) )
				goto out_loop;
			argptr++;
		}
		ReplyMsg((struct Message *) amsg);
	}
	out_loop:
	ActivateWindow(window);
	RefreshGList  (strgad, window, NULL, 1);
	ActivateGadget(strgad, window, NULL);
}

/**** Main loop, which collects and distributes all messages: ****/
void handle_input(void)
{
	extern ULONG sigwin, sigappwnd;
	ULONG sigrcvd;
	BOOL  quit = 0;

	do {
		sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag | sigwin | sigappwnd);

		/* Look from where the signal comes: */
		if(sigrcvd & SIGBREAKF_CTRL_C) cleanup(RETURN_OK);

		else if(sigrcvd & cxsigflag) quit = Handle_port();
		
		else if(sigrcvd & sigappwnd) Handle_wbapp();

		else if((sigrcvd & sigwin) == 0) continue;

		while (msg = (void *) GT_GetIMsg(window->UserPort))
		{
			/* Copy entire message */
			CopyMemQuick(msg, &msgbuf, sizeof(msgbuf));
			GT_ReplyIMsg(msg);

			switch(msgbuf.Class)
			{
				/* The close-window gad. simply hide the main window */
				case IDCMP_CLOSEWINDOW: cleanup(RETURN_OK);
				case IDCMP_MENUPICK:
				{	struct MenuItem *menuitem;

					while (msgbuf.Code != MENUNULL)
					{
						menuitem = (struct MenuItem *) ItemAddress(menu,msgbuf.Code);
						quit     = Handle_menu( menuitem );
						msgbuf.Code = menuitem->NextSelect;
					}
				}	break;

				case IDCMP_VANILLAKEY:
					quit = Handle_kbd( msgbuf.Code );
					break;
				case IDCMP_GADGETUP:
					quit = Handle_gadget( ((struct Gadget *)msgbuf.IAddress)->GadgetID );
					break;
			}
		}
	} while( quit == 0 );
}
