/******************************************************************************
 *
 *  THIS FILE USES 4 COLUMN TAB STOPS. 
 *	
 *
 *	This is the Intuition Message processor for Leach Ver 1.3
 *
 ******************************************************************************/

#include	<functions.h>
#include	<exec/ports.h>
#include	<exec/libraries.h>
#include	<graphics/gfx.h>
#include	<graphics/rastport.h>
#include	<graphics/text.h>
#include	<intuition/intuitionbase.h>
#include 	<stdio.h>

/*----------------------------------------------------------------------------*
 * gad.h is a real mixed bag. It defines (allocates storage for) many gadget
 * related variables. It also declares the char array "statustext".
 *----------------------------------------------------------------------------*/

#include	"gad.h"


/*  Stuff in globals.h is defined globally and initialized in main.c.	*/

#define GLOBAL extern
#include "globals.h"

extern short endpoints[];			/* End point coordinates for ruler	*/

#define x1 0						/* endpoints array indexes.			*/
#define y1 1						/* Note LOWER CASE!					*/
#define x2 2
#define y2 3

extern struct	Border ruler;		/* Border struct for ruler.			*/

/************  VARIABLES USED JUST INSIDE THIS FILE  ********************/

short	mx, my;			/* Mouse coordinates.	*/
short	trac;			/* TRUE if in the middle of MOUSEBUTTONS pair	*/
short	update;			/* Flag means status display needs refreshing.	*/
short	ruler_color = 3;
short	moving = 0;		/* Nonzero if ruler should be tracking mouse*/
short	sizing = 0;		/* 0 if the ruler is not being sized/moved	*/	
						/* 1 if the first endpoint is being moved.	*/
						/* 2 if the second endpoint is being moved.	*/


/*******************************************************************************
 *						LOOP MONITORING MENU EVENTS
 *
 * This routine sleeps, waiting for an IntuiMsg for either the Host or for
 * the Leach's status window or for a message being returned by the Host to
 * "MyPort". There are separate processor routines for each of these message
 * types. Note that ALL MESSAGES IN THE HOST IDCMP ARE PROCESSED BEFORE ANY
 * OF THE LEACH MESSAGES ARE PROCESSED. Hopefully this means that there
 * won't be any msgs for Leach in the Host's queue when Leach processes
 * a CLOSEWINDOW event from its queue. Eventually one of the processors will
 * fail to return because it terminated the program. event_processor() will
 * never return to its caller.
 *
 * Also remember that Leach is running at a higher priority than the Host
 * so, Leach will get first crack at the incoming messages even if
 * the Host is processing its msg queue when a msg arrives at the port.
 ******************************************************************************/

event_processor()
{

ULONG	sigfor, StatMask, HostMask, MyMask;

StatMask = 1L << StatWind->UserPort->mp_SigBit;
HostMask = 1L << HostSig;
MyMask	 = 1L << MySig;

FOREVER
{
	sigfor = Wait(HostMask | StatMask | MyMask);

	if (sigfor & MyMask)	/* Replying to IntuiMsgs gets top priority.	*/
		myport_event();
	if (sigfor & HostMask)
		host_event();
	if (sigfor & StatMask)
		stat_event();
	if (sigfor & ~(HostMask | StatMask) )
	{
		puts("event_processor(): RECEIVED AN UNEXPECTED SIGNAL BIT.");
		cleanup(202);
	}

} /* end of FOREVER */

} /*  End of event_processor()  */


/*****************************************************************************
 * CHECKMSG()
 *
 * This routine looks at the messages in the Host IDCMP's input queue and
 * returns the address of the next message to be processed by host_event().
 * It always starts at the first msg in the queue, walking the linked list
 * until it finds a msg whose mn_ReplyPort is not "MyPort". It sets the
 * mn_ReplyPort to MyPort in messages that have been processed. It has to 
 * work this way cause you never know when a new msg might arrive or what 
 * the Host has been doing to the list since you last looked at it.
 *
 * Returns address of current message or Null if there are no new messages.
 *
 * FYI: This is what an IntuiMessage looks like.
 *
 * struct IntuiMessage
 * {
 *	struct Message ExecMessage 
 *	{
 *		struct Node	mn_Node
 *		{
 *			struct Node *ln_Succ;
 *			struct Node *ln_Pred;
 *			UBYTE	    ln_Type;
 *			BYTE	    ln_Pri;			NOT ALWAYS INITIALIZED TO ZERO!
 *			char	    *ln_Name;
 *		}
 *		struct MsgPort	*mn_ReplyPort;
 *		UWORD			mn_Length;
 *	}
 *	ULONG	Class;
 *	USHORT	Code;
 *	USHORT	Qualifier;
 *	APTR	IAddress;
 *	SHORT	MouseX, MouseY;
 *	ULONG	Seconds, Micros;
 *	struct Window *IDCMPWindow;
 *	struct IntuiMessage *SpecialLisk;		Don't ever mess with this
 * }
 *
 *	struct MsgPort
 *	{
 *		struct	Node	mp_Node;
 *		UBYTE			mp_Flags;
 *		UBYTE			mp_SigBit;
 *		struct	Task	*mp_SigTask;
 *		struct	List 	mp_MsgList;
 *	};
 *****************************************************************************/

struct IntuiMessage *checkmsg(port)
struct MsgPort	*port;				/* Port to be examined	*/
{

register struct IntuiMessage *message;

short	cnt;						/* cnt & check_flags are for debugging */
short	check_flags;


/* These #defines are only used for debugging.	*/

#define NO_MSG		0x0001
#define NULL_HEAD	0x0004
#define NO_NEW		0x0008

cnt = check_flags = 0;
Forbid();					/*##########  MULTITASKING DISABLED  ##########*/


/*-------------------------------------------------------------------------*
 * Aim message pointer at the first node in the list. lh_Head should never
 * be NULL, but we'll check anyway. If Head is pointing at the Tail, the
 * msg queue is empty. This could be an error but probably isn't. It
 * happens when disposemsg() Remove()s the only message in the queue. In
 * this case, checkmsg() returned a valid message address so, the while 
 * loop in host_event() takes another look at the message queue, which is 
 * now empty.
 *-------------------------------------------------------------------------*/

message = (struct IntuiMessage *) port->mp_MsgList.lh_Head;
if (!message)
	check_flags |= NULL_HEAD;
else if (message == &(port->mp_MsgList.lh_Tail) )
{
	check_flags |= NO_MSG;
	message = NULL;
}
while (message)
{
	++cnt;
	if (message->ExecMessage.mn_ReplyPort != MyPort)
	{
		/*--------------------------------------------------------------------*
		 * Found unprocessed message. I hate altering the IntuiMessage like
		 * this but, you can't count on the date stamps uniquely identifying
		 * the messages. I had to mark them somehow.
		 *--------------------------------------------------------------------*/
		if (!HostIPort)
		{
			HostIPort = message->ExecMessage.mn_ReplyPort;
		}
		else if (message->ExecMessage.mn_ReplyPort != HostIPort)
		{
			/* Aegis Images triggers this error message sometimes.	*/
			puts("CHECKMSG(): Found an unexpected mn_ReplyPort.");
		}
		message->ExecMessage.mn_ReplyPort = MyPort;
		break;
	}
	else	/* Leach has already seen this msg. Try next msg in the queue.	*/
	{
		message = message->ExecMessage.mn_Node.ln_Succ;
		if (message == &(port->mp_MsgList.lh_Tail) )
		{
			message = NULL;					/* Reached end of msg list.	*/
			check_flags |= NO_NEW;
		}
	}
} /*  end of while (message)  */


/*------------------------------------------------------------------------*
 * Re-enabling multitasking here feels vaguely dangerous. I am about to
 * pass around a pointer to a msg that I don't own. However, host_event()
 * will make a copy of the message data as soon as checkmsg() returns.
 *------------------------------------------------------------------------*/

Permit();					/*###########  MULTITASKING ENABLED  ###########*/

/*****************************************************************************
 *** If any of the "error" conditions occurred, now's the time to speak up.  *

if (check_flags)
{
	if (check_flags & NO_MSG)
	{
		puts("No msg in Host's UserPort.");
	}
	if (check_flags & NULL_HEAD)
	{
		puts(" SOME HOW, LH_HEAD IS NULL."); 
	}
	if (check_flags & NO_NEW)
	{
		printf("EOQ #%d.\n", cnt);
	}
	check_flags = 0;
}
else
{
	printf("checkmsg(): Msg #%d new.\n", cnt);
}
printf("checkmsg(): Returning msg addr $%lx.\n", message);
*****************************************************************************
*****************************************************************************/

return(message);

} /*  End of checkmsg()  */



/*****************************************************************************
 * This routine identifies messages that Leach is not going to pass on
 * to the Host, Remove()'s them, restores mn_ReplyPort to Intuition's Port 
 * address & ReplyMsg()'s to them. It is also responsible for Signal()'ing
 * the Host for msgs that are not consumed by Leach.
 *****************************************************************************/

disposemsg(msg)
struct IntuiMessage	*msg;
{

register ULONG	class;
register USHORT	code;

class = msg->Class;
code  = msg->Code;

/*---------------------------------------------------------------------------*
 * If any of 3 criteria for consuming a msg is met the msg is removed from 
 * the queue and replied to. The criteria are:
 *		1) A MOUSEBUTTONS event while the ruler is in "move" mode.
 *		2) A MOUSEMOVE event while the ruler is tracking the mouse.
 *		3) A menu selection has been made from the menu Leach installed
 *		   at the end of the Host's first menu.
 *---------------------------------------------------------------------------*/

if ( ( (MOVEGAD.Flags & SELECTED) && (class == MOUSEBUTTONS) ) ||
	   ( (trac == TRUE) && (class == MOUSEMOVE ) )  ||
	   ( (class == MENUPICK) && (code  != MENUNULL) &&
		 ( MENUNUM(code) == L_menu_num) && (ITEMNUM(code) == L_menuitem_num) ) )
{
	Forbid();
	Remove( &(msg->ExecMessage.mn_Node) );		/* For Leach only.	*/
	Permit();
	msg->ExecMessage.mn_ReplyPort = HostIPort;
	ReplyMsg(msg);
}
else
{
	Signal(HostTask, 1L << HostSig);
}

return;

}  /*  End of disposemsg()  */


/*****************************************************************************
 *					HOST INTUITION EVENT PROCESSOR
 * This routine examins all the messages waiting at the Host's IDCMP. The
 * messages have to be examined in place so that they can be passed on to
 * the Host, if necessary. Messages intended exclusively for Leach are
 * removed from the message queue.
 *****************************************************************************/

host_event()
{

ULONG	class;		 /* Class of IntuiMessage.	*/
USHORT	code;		 /* Code of IntuiMessage.	*/

static UBYTE act_flag = 0;	/* Set when Host receives ACTIVEWINDOW msg.	*/

USHORT	MenuNum, ItemNum, SubNum; /* Menu, MenuItem and Subitem numbers */
short	mmflag;
struct IntuiMessage	*message;

/*----------------------------------------------------------------------*
 * Note that checkmsg() is periodically called when there are no msgs in
 * the queue. This happens when disposemsg() removes the only msg in the
 * queue or when the end of the queue is reached. checkmsg() gets called
 * one last time and finds the cupboard is bare. This is not an error.
 *----------------------------------------------------------------------*/

while( message =  checkmsg(HostUPort) )
{
	class = message->Class;
	code  = message->Code;
	mx    = message->MouseX;
	my    = message->MouseY;

	/* printf("H_event: Class = $%lx. Code = $%x.\n\n", class, code); */

	disposemsg(message);		/* ReplyMsg()s to Leach msgs and Signals */
								/* the Host for pass through msgs.		 */


	if (class == ACTIVEWINDOW)
		act_flag = 2;			/* Ignore next SELECT UP/DOWN pair.		*/

	/*---------------------  HOST MENU EVENTS  -------------------------*
	 *------------------------------------------------------------------*/

	else if (class == MENUPICK && code != MENUNULL) 
	{
		MenuNum = MENUNUM(code);
		ItemNum = ITEMNUM(code);
		SubNum	= SUBNUM(code);

		/* If Leach's MenuItem...	*/
		if (MenuNum == L_menu_num && ItemNum == L_menuitem_num)
		{
			switch (SubNum)
			{
				case 0:							/* Leach screen to front */
					ScreenToFront(StatScreen);
					break;

				case 1:							/* Leach screen to back	 */
					ScreenToBack(StatScreen);
					break;
			}
		}
		else									/* Not Leach Menu		 */
		{
			/*----------------------------------------------------------*
			 * The only Host menu event that we take special interest in
			 * is QUIT. All other Host menu events are treated the same 
			 * way. Namely, Leach tries to make itself as inconspicuous
			 * as possible.
			 *----------------------------------------------------------*/

			if ( (MenuNum == H_quit_menu) && (ItemNum == H_quit_item) &&
														(H_found_quit)   )
			{
				cleanup(204);
			}
			else
			{
				if (SHOWGAD.Flags & SELECTED)  /* If in show mode... */
				{
					hide_ruler();
				}
				ScreenToBack(StatScreen);
				break;
			}
		}

	} /* End of if (class == MENUPICK && code != MENUNULL)	*/


	/*-------------------  SELECT BUTTON EVENTS  -----------------------*
	 * Select button events while the MOVE gadget is selected are the
	 * exclusive property of Leach. They control movement and sizing of
	 * the ruler. If the Host window activation flag is set, we want to
	 * ignore the next mouse click.
	 *------------------------------------------------------------------*/

	else if ( (class == MOUSEBUTTONS) && (MOVEGAD.Flags & SELECTED) )
	{
		if (act_flag)
			--act_flag;
		else if (code == SELECTDOWN)
		{
			HostFlagsmods |= REPORTMOUSE;
			HostWind->Flags = HostFlags | HostFlagsmods;

			HostIDCMPmods |= MOUSEMOVE;
			ModifyIDCMP(HostWind, HostIDCMPflags | HostIDCMPmods);

			trac = TRUE;

			/* If the mouse is near the first endpoint, then move it.	*/

			if ( (mx > endpoints[x1] - 4) && (mx < endpoints[x1] + 4) &&
			     (my > endpoints[y1] - 5) && (my < endpoints[y1] + 5)	)
			{
				sizing = 1;
				moving = 0;
				size_ruler();
			}

			/* Or, if the mouse is near the second endpoint, then move it. */

			else if ( (mx > endpoints[x2] - 4) && (mx < endpoints[x2] + 4) &&
					  (my > endpoints[y2] - 5) && (my < endpoints[y2] + 5)	)
			{
				sizing = 2;
				moving = 0;
				size_ruler();
			}
			
			/* By default we'll move the entire ruler. The first endpoint */
			/* will jump to the mouse position.							  */

			else	
			{
				sizing = 0;
				moving = 1;
				move_ruler();
			}
		} /*  end of if ( (code == SELECTDOWN) && etc )  */

		else if (code == SELECTUP)
		{
			/*----------------------------------------------------------*
			 * If we're done moving, shut off reports on mouse movement.
			 * There may be other mouse movement msgs at the IDCMP but,
			 * we'll process/ignore them later.
			 *----------------------------------------------------------*/

			HostFlagsmods  &= ~REPORTMOUSE;
			HostWind->Flags = HostFlags | HostFlagsmods;

			HostIDCMPmods	&= ~MOUSEMOVE;
			ModifyIDCMP(HostWind, HostIDCMPflags | HostIDCMPmods);
			trac = FALSE;
		}
	} /*  end of else if ( (class == MOUSEBUTTONS) && etc)  */


	/*----------------------  RULER MOVEMENT  -----------------------------*
	 * If the ruler is tracking the mouse, all we do is note that the mouse
	 * has moved. We don't actually move the ruler until we've processed
	 * all the messages in the queue, other wise we couldn't handle the
	 * flood of MOUSEMOVE messages that come in.
	 *---------------------------------------------------------------------*/

	else if ( (class == MOUSEMOVE) && (trac == TRUE) )
	{
		mmflag	= TRUE;

	} /*  end of else if (class == MOUSEMOVE && etc)  */

} /*  End of while(message =  checkmsg(HostUPort))  */


/*----------------------------------------------------------------------------*
 * At this point we have processed all of the new msgs that were found in the
 * Host's message queue. If we have encountered any MOUSEMOVE msgs during this
 * pass through the list that were for Leach, we'll now actually move the
 * ruler and change the status display.
 *----------------------------------------------------------------------------*/

if (mmflag)		/* If there have been requests for line movement.	*/
{
	if (moving)
		move_ruler();
	else if (sizing)
		size_ruler();

	mmflag = FALSE;
}
/*---------------------------------------------------------------------------*
 * trac is cleared when a SELECTUP event is detected. This tells disposemsg()
 * to stop scarfing MOUSEMOVE msgs. However, there may still be a move 
 * pending as indicated by moving or sizing. Well, we just finished that
 * move, so now we can finally turn these indicators off.
 *---------------------------------------------------------------------------*/

if (!trac)
{
	moving = sizing = 0;
}
	
if (update)
{
	update_status();
	update = 0;
}

return;

} /*  End of host_event  */


/******************************************************************************
 *					LEACH INTUITION EVENT PROCESSOR
 * This routine is a more conventional message processor for the Status 
 * Display Window. We're expecting only Close Window and Gadget events
 * at the moment.
 *
 ******************************************************************************/

stat_event()
{

ULONG			class;		/* Class of IntuiMessage.					*/
USHORT			code;		/* Code of IntuiMessage & MenuItem number.	*/
struct	Gadget	*gad_addr;	/* Address of toggled gadget.				*/

struct	IntuiMessage	*message;

while ( message = (struct IntuiMessage *) GetMsg(StatWind->UserPort) )
{
	class 	 = message->Class;
	code  	 = message->Code;
	mx    	 = message->MouseX;
	my    	 = message->MouseY;
	gad_addr = (struct Gadget *) message->IAddress;
	
	ReplyMsg(message);

	if (class == CLOSEWINDOW)
	{
		cleanup(206);
	}
	else if (class == GADGETUP)
	{
		/*******************  Toggle the SHOW/HIDE" Gadget ?  ***************/

		if (gad_addr == &SHOWGAD)			/* If the show/hide gadget	*/
		{
			/*  If ruler just got deselected enter hide mode.	*/
			if (! (SHOWGAD.Flags & SELECTED) )
			{
				hide_ruler();
			}
			else
			{
				/*----------------------------------------------------------*
				 * S/H has just changed to show mode. Change displayed text
				 * Draw ruler. Make sure that M/F gadget is enabled
				 *----------------------------------------------------------*/

				swap_gtext(gad_addr);
				draw_line(HostRPort, ruler_color, endpoints[x1], endpoints[y1],
												  endpoints[x2], endpoints[y2]);
				OnGadget(&MOVEGAD, StatWind, NULL);
			}
		} /*  end of if (gad_addr == &SHOWGAD)  */


		/************* Toggled the "MOVE/FREEZE" Gadget ?  *****************/

		else if (gad_addr == &MOVEGAD)
		{
			swap_gtext(&MOVEGAD);

			if (MOVEGAD.Flags & SELECTED)	/* Entering move mode.	*/
			{
				/* Enable selectbutton reports.	*/
				HostIDCMPmods |= MOUSEBUTTONS;
				ModifyIDCMP(HostWind, HostIDCMPflags | HostIDCMPmods);
				set_pointer();
			}
			else	/* Entering freeze mode. Text = "Move" */
			{
				HostIDCMPmods &= ~(MOUSEBUTTONS | MOUSEMOVE);
				ModifyIDCMP(HostWind, HostIDCMPflags | HostIDCMPmods);
				restore_pointer();
			}
		}
		else if (gad_addr == &swind_gad[2])		/* If the left color gadget	*/
		{
			if (SHOWGAD.Flags & SELECTED)	/*  If ruler is visible...	*/
			{
				restore_line();
				--ruler_color;
				draw_line(HostRPort, ruler_color, endpoints[x1], endpoints[y1],
												  endpoints[x2], endpoints[y2]);
			}
		}
		else if (gad_addr == &swind_gad[3])		/* If the rite color gadget	*/
		{
			if (SHOWGAD.Flags & SELECTED)	/*  If ruler is visible...	*/
			{
				restore_line();
				++ruler_color;
				draw_line(HostRPort, ruler_color, endpoints[x1], endpoints[y1],
												  endpoints[x2], endpoints[y2]);
			}
		}
		else
		{
			puts("Leach received a funny gadget address.");
		}

		RefreshGadgets(swind_gad, StatWind, NULL);

	} /*  end of else if (class == GADGETUP)  */

	else if (class == REFRESHWINDOW) /* Shouldn't get any of these,	*/
	{								 /* but what the Hell.			*/
		BeginRefresh(StatWind);
		EndRefresh(StatWind, TRUE);		
		update_status();
		RefreshGadgets(swind_gad, StatWind, NULL);
		update = 0;
	}
	else
	{
		puts("stat_event(): Received unexpected Msg class.");
	}
} /*  end of while (message = etc.)  */

return;

} /*  End of stat_event()  */


/**************************************************************************
 * Restore the mn_ReplyPort address in a message returned by the Host and
 * forward to Intuition.
 **************************************************************************/

myport_event()
{
struct	IntuiMessage	*msg;

while ( msg = (struct IntuiMessage *) GetMsg(MyPort) )
{
	if (HostIPort)
	{
		msg->ExecMessage.mn_ReplyPort = HostIPort;
		ReplyMsg(msg);
	}
	else
		puts("MYPORT_EVENT(): Trouble in River City.");
}
return;

} /*  End of myport_event()  */


/**************************************************************************
 * Exchange gadget IntuiText text pointer with gadget's UserData pointer
 * which, in this case, points to the alternate text string.
 **************************************************************************/

swap_gtext(gad_addr)
struct Gadget	*gad_addr;
{

UBYTE *itext;	/* Temp storage for alternate text pointer during swap.	*/

itext 						= gad_addr->UserData;
gad_addr->UserData 			= gad_addr->GadgetText->IText;
gad_addr->GadgetText->IText = itext;

return;

} /*  End of swap_gtext()  */

/*********************  END OF EVENT.C FOR LEACH PROGRAM  *********************/
