/***************************************************************
** Events.c: Prends en charge la gestion des événements uti-  **
**           sateur. Écrit par T.Pierron                      **
**           03-08-1999                                       **
***************************************************************/

#include <Intuition/Intuition.H>
#include <Intuition/IntuitionBase.H>
#include <Graphics/RastPort.H>
#include <Graphics/GfxBase.H>
#include <Graphics/Text.H>
#include <Libraries/Gadtools.H>
#include <Libraries/Commodities.h>
#include <Libraries/Dos.h>
#include <Intuition/Screens.H>

/* External variables: */
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
extern struct Library *IconBase;
extern struct Window *window;
extern struct Screen *screen;
extern struct NewMenu newmenu[],*checked;
extern struct TextFont *font,*newfont;
extern struct RastPort *RP;
extern struct Gadget *gad;
extern struct Menu *menu;
extern WORD   xdeb,ydeb,xfin,yfin,X,Y,I;		/* ASCII Table position */
extern UBYTE  MaxWid,Font_height,Char[];		/* Font information */
extern ULONG  BoxTags[],sigwin;

/* Commodity handler: */
struct TTextAttr *aslfont;							/* Returned by the ASL font requester */
struct MsgPort *broker_mp = NULL;				/* Commodity message port */
struct MenuItem *menuitem;							/* Pointer to allocated menu bar */
CxObj  *broker, *filter;							/* Commodity and hot-key */
CxMsg  *msg;											/* To collect messages */


BOOL  IsDraw=0,PopWin=TRUE;						/* State of the recessed box */
UBYTE NumChar;											/* Selected character number */
ULONG cxsigflag=0;									/* Signal bits of the commo. */


/* Different values, to handle the recessed box while pressing lmb: */
#define	NOT_PRESSED			0
#define	PRESSED				1
#define	OUTSIDE_AREA		2
#define	EVT_HOTKEY			1L


struct NewBroker newbroker = {
	NB_VERSION,
	"CharMap",							/* String to identify this commo. */
	"Un petit utilitaire permettant d'afficher",
	"l'ensemble des caractères ASCII d'une police",
	NBU_UNIQUE | NBU_NOTIFY |		/* Don't want any new commodities starting with this name. */
	COF_SHOW_HIDE,						/* If someone tries it, let me know */
	0, 0, 0, 0
};

void Free_commodity();

/**** Initialize the message port of the commodity: ****/
BOOL Init_commodity(int argc, char *argv[])
{
	UBYTE *hotkey, **ttypes;

	if(broker_mp = (void *) CreateMsgPort())
	{
		newbroker.nb_Port = broker_mp;
		cxsigflag         = 1L << broker_mp->mp_SigBit;
		ttypes            = (UBYTE **) ArgArrayInit(argc, argv);
		newbroker.nb_Pri  = (BYTE)ArgInt(ttypes, "CX_PRIORITY", 0);
		hotkey            = (UBYTE *) ArgString(ttypes, "HOTKEY", "rawkey lalt control c");

		/* Whould the user show immediatly the window ? */
		if( strcasecmp(ArgString(ttypes, "CX_POPUP", ""), "NO")==0 ) PopWin=FALSE;

		if(broker = (void *) CxBroker(&newbroker, NULL))
		{
			/* HotKey() is an amiga.lib function that creates a filter, sender */
			/* and translate CxObject and connects them to report a hot key    */
			/* press and delete its input event. */
			if(filter = (void *) HotKey(hotkey, broker_mp, EVT_HOTKEY))
			{
				AttachCxObj(broker, filter);		/* Add a CxObject to another's personal list */

				if(! CxObjError(filter))
					ActivateCxObj(broker, 1L);		/* All done! */
			}
		}
		ArgArrayDone();  /* this amiga.lib function cleans up after ArgArrayInit() */
	}

	return (broker && broker_mp);
}

void Free_commodity()
{
	/* DeleteCxObjAll() is a commodities.library function that not only      **
	** deletes the CxObject pointed to in its argument, but deletes all of   **
	** the CxObjects attached to it.                                         */
	if(broker)
	{
		DeleteCxObjAll(broker);

		/* Empty the port of all CxMsgs */
		while(msg = (CxMsg *)GetMsg(broker_mp))
			ReplyMsg((struct Message *)msg);
	}

	if(broker_mp) DeletePort(broker_mp);
}

/**** Move the sunken box while dragging the mouse with LMB down: ****/
void Handle_box()
{
	static WORD OldX=0,OldY=0;
	WORD sunken=FALSE;

	/* Center mouse position on top-left corner of the box: */
	if(X<xdeb) X=0; else X -= (X-xdeb)%MaxWid;
	if(Y<ydeb) Y=0; else Y -= (Y-ydeb)%Font_height;

	/* If it isn't already drawn... */
	if(X==OldX && Y==OldY) return;
	BoxTags[2] = TAG_DONE;

	/* ...and inside area */
	if(OldX>=xdeb && OldX<xfin && OldY>=ydeb && OldY<yfin)
		DR:DrawBevelBoxA(RP,OldX,OldY,MaxWid,Font_height,BoxTags);

	if(sunken) return; OldX=X; OldY=Y; IsDraw=PRESSED;

	if(X>=xdeb && X<xfin && Y>=ydeb && Y<yfin) {
		BoxTags[2] = GTBB_Recessed;
		NumChar = ((X-xdeb)/MaxWid<<3)+(Y-ydeb)/Font_height+32;
		sunken=TRUE; goto DR;
	} else {
		NumChar=0;
		IsDraw=OUTSIDE_AREA;
	}
}

/**** Process menus events: ****/
void Handle_menu( LONG MenuID )
{
	switch( MenuID )
	{
		case 11:	/* Regarde si le premier item n'est pas déjà sélectionné: */
					if(checked == &newmenu[1]) break;
					/* Prends la police de l'écran (A ne pas refermer donc!): */
					font = screen->RastPort.Font; goto check;
		case 12:	/* Même combat: */
					if(checked == &newmenu[2]) break;
					font = GfxBase->DefaultFont;
		check:	/* Les menus vont être recréés, change directement les NewMenus: */
					checked->nm_Flags &= ~CHECKED;
					checked = &newmenu[ MenuID-10 ];
					checked->nm_Flags |= CHECKED;
					/* On peut réouvrir la fenêtre: */
					goto new_win;
		REDOIT:	MenuID = 13;
		case 13:	/* Open a ASL requester: */
					if( !(aslfont = (struct TTextAttr *) open_asl()) ) break;
					if( newfont ) CloseFont(newfont);
					if( !(newfont=(void *)OpenDiskFont(aslfont)) ) break;
					font = newfont;
					goto check;
		case 14:	/* Efface le texte du gadget de chaine: */
					((struct StringInfo *)gad->SpecialInfo)->Buffer[0]=0;
					RefreshGList(gad, window, NULL, 1);
					break;
		case 15:	/* Passe à l'écran suivant dans la liste: */
					if( !screen->NextScreen ) break;
					screen = screen->NextScreen;
					if(checked == &newmenu[1]) font = screen->RastPort.Font;
		new_win:	cleanup(NULL,-1);
					/* Si l'installation échoue, on recommence: */
					if( setup() ) goto REDOIT;
					/* On attends d'être sûr que tout soit OK pour avancer l'écran: */
					ScreenToFront(screen);
					break;
		case 16:	about_app(); break;
		case 17: /* Iconifie/déiconifie la fenêtre: */
					ZipWindow(window);
					ActivateCxObj(broker, window->Height>window->BorderTop); break;
		case 18:	cleanup(NULL,-1); return;
		case 19:	cleanup(NULL,0);
	}
	if(window==NULL) cleanup(0,0);
}

/**** Handle all messages comming from a commo: ****/
void Handle_commo(void)
{
	ULONG msgid, msgtype;

	/* The signal is comming from a commodity: */
	while(msg = (CxMsg *)GetMsg(broker_mp))
	{
		msgid = CxMsgID(msg);
		msgtype = CxMsgType(msg);
		ReplyMsg((struct Message *)msg);

		switch(msgtype)
		{
			case CXM_IEVENT:
				/* Display or move the window, in the front-most screen: */
				if(msgid == EVT_HOTKEY)
				{
					screen = IntuitionBase->ActiveScreen;
					/* If the window is already open, but not in the front-most screen, close it: */
					if(window)
						if(window->WScreen != screen) cleanup(NULL,-1);
						else break;

					if(checked == &newmenu[1]) font = screen->RastPort.Font;
					/* If something goes wrong, change the font: */
					if( setup() ) Handle_menu(13);
					ScreenToFront(screen);
				}
				break;
			case CXM_COMMAND:
				switch(msgid)
				{
					case CXCMD_DISABLE:
						/* If user want to disable the commo, iconify the window: */
						if(window->Height>window->BorderTop) ZipWindow(window);
						ActivateCxObj(broker, 0L);
						break;
					case CXCMD_ENABLE:
						/* If user want to enable the commo, uniconify it: */
						if(window->Height<=window->BorderTop) ZipWindow(window);
						ActivateCxObj(broker, 1L);
						break;

					case CXCMD_APPEAR:	if(window==NULL && setup())
													Handle_menu(13);				break;
					case CXCMD_DISAPPEAR:if(window) cleanup(NULL,-1);	break;
					case CXCMD_KILL:		cleanup(0,0);

					case CXCMD_UNIQUE:
						/* Commodities Exchange can be told not only to refuse to launch a   **
						** commodity with a name already in use but also can notify the      **
						** already running commodity that it happened.  It does this by      **
						** sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE.  If the   **
						** user tries to run a windowless commodity that is already running, **
						** the user wants the commodity to shut down.                        */
						cleanup(0,0);
						break;
				}
		}
	}
}

/**** Main loop, which collects and distribute all messages: ****/
void handle_input(void)
{
	struct IntuiMessage *message;
   struct MenuItem *item;
	struct Gadget *gadget;
	ULONG class;
	UWORD code,HelpWin=FALSE;
	ULONG sigrcvd;

	FOREVER {
Deb:	sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag | sigwin);

		/* Look from where the signal has been launched: */
		if(sigrcvd & SIGBREAKF_CTRL_C) cleanup(0,0);

		if(sigrcvd & cxsigflag) Handle_commo();

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

		/* Use the new GadTools GT_GetIMsg() function to get input events */
		while (message = (void *) GT_GetIMsg(window->UserPort))
		{
			/* Interesting values: */
			class = message->Class;	X = message->MouseX;
			code = message->Code;	Y = message->MouseY;

			/* Is it a mouse button or a menu call? */
			if( code == MENUHOT && Y>=ydeb && Y<=yfin && X>=xdeb && X<=xfin) {
				/* The right mouse button has been pressed over the ASCII table: */
				message->Code=MENUCANCEL;
				class=MOUSEBUTTONS;
				window->Flags |= RMBTRAP;
			}

			/* Reply to the message */
			GT_ReplyIMsg(message);

			switch (class)
			{
				/* The close-window gad. simply quit: */
				case CLOSEWINDOW:	cleanup(NULL,0);
				case MOUSEBUTTONS:
					switch (code)
					{
						/* Draw a recessed box below the mouse: */
						case SELECTDOWN: if(HelpWin==FALSE) Handle_box(); break;
						/* Clear the box and add the char. in string gadget: */
						case SELECTUP:
							if(IsDraw) {
								if( (*Char = NumChar) && IsDraw==TRUE) {
									strcat( ((struct StringInfo *)gad->SpecialInfo)->Buffer,Char);
									RefreshGList(gad, window, NULL, 1);
								}
								CL:X=Y=0; Handle_box(); IsDraw=NOT_PRESSED;
							}
							break;
						/* Draw a little window and show information on the char.: */
						case MENUHOT:
							if(IsDraw) goto CL;
							else { 
								Handle_box();
								if(NumChar) { HelpWin=TRUE; Open_helpwin(NumChar,X-1,Y-1); }
								else window->Flags &= ~RMBTRAP;
								IsDraw=NOT_PRESSED;
							}
							break;
						/* Clear the help window: */
						case MENUUP:
							window->Flags &= ~RMBTRAP;
							if(HelpWin) { HelpWin=FALSE; Close_helpwin(); goto CL; }
					}
					break;
						
				case MOUSEMOVE: if(IsDraw && !HelpWin) Handle_box(); break;

				case MENUPICK:
					menuitem = (struct MenuItem *) ItemAddress(menu,code);
					Handle_menu( GTMENUITEM_USERDATA(menuitem) );
					break;

				case VANILLAKEY:		/* Translated keycode */
					if(code==27) cleanup(NULL,IconBase && !(message->Qualifier&IEQUALIFIER_LSHIFT)?-1:0);
					if(code=='\b' || code==127) {
						register WORD n=(code=='\b'?((struct StringInfo *)gad->SpecialInfo)->NumChars-1:0);
						if(n<0) break;
						((struct StringInfo *)gad->SpecialInfo)->Buffer[n]=0;
						RefreshGList(gad, window, NULL, 1);
					}
					break;

				case REFRESHWINDOW:
					/* A iconified window has been poped-up: */
					if(window->Height>Font_height) Draw_ASCIIChart();
			}
			/* Has the user hiden the window ? */
			if(window==NULL) goto Deb;
		}
	}
}
