/*
 *		HOTKEY.C												vi:ts=4
 *
 *      Copyright (c) Eddy Carroll, September 1994.
 *
 *		Controls the management of the commodities hotkey for SnoopDos,
 *		along with other related stuff (Workbench AppIcon/Tools Menu support).
 */

#include "system.h"
#include "snoopdos.h"
#include "snooptext.h"

#include "icon.h"

extern char CommodityTitle[];	/* From SNOOPDOS.C */

struct NewBroker MyBroker = {
	NB_VERSION,
	"SnoopDos",
	CommodityTitle,
	NULL,						/* Description, filled in at run time */
	NBU_UNIQUE | NBU_NOTIFY,
	COF_SHOW_HIDE,
	0, 0, 0
};

struct MsgPort		*BrokerMP;		/* Message port used for msgs from CX	*/
CxObj				*Broker;		/* CX handle for the broker we created	*/
CxObj				*Filter;		/* Filter to filter CX messages			*/
struct MsgPort		*WorkbenchPort;	/* Port for AppIcon/AppMenu messages	*/
struct DiskObject	*ProgramIcon;	/* Handle on our program's icon data	*/
struct AppMenuItem	*MyAppMenu;		/* Current AppMenuItem, if any			*/
struct AppIcon		*MyAppIcon;		/* Current AppMenuItem, if any			*/
struct DiskObject	*AppIconObj;	/* Current object for AppMenuItem		*/	
struct DiskObject	*DefIconObj;	/* A temporary default icon we use		*/	

/*
 *		InstallHotKey(char *hotkey)
 *
 *		Initialises commodity support and sets things up so that the
 *		key sequence specified by "hotkey" will send a CTRL-F activation
 *		signal to SnoopDos. We can also get various messages from the
 *		commodities exchange to show/hide the interface etc -- these
 *		will be indicated using CommodityMask.
 *
 *		To see whether a hotkey is currently available or not, simply
 *		check the HotKeyActive flag. (This is used for disabling window
 *		gadgets etc.)
 *
 *		Returns true for success, false for failure.
 */
int InstallHotKey(char *hotkey)
{
	CxObj *newobj;

	HotKeyActive = 0;		/* Assume commodity is not currently active */

	if (CurSettings.Setup.HideMethod == HIDE_NONE || !CxBase) {
		/*
		 *		Don't install a commodity at all if we have no hide
		 *		method.
		 */
		return (0);
	}
	if (!BrokerMP) {
		BrokerMP = CreateMsgPort();
		if (!BrokerMP)
			return (0);
		CommodityMask	  = (1L << BrokerMP->mp_SigBit);
	}

	if (Broker && MyBroker.nb_Pri != CommodityPriority) {
		/*
		 *		The commodity priority has changed, so 
		 *		remove the current broker to force a new
		 *		one to be created at the correct priority.
		 */
		DeleteCxObjAll(Broker);
		Broker = NULL;
		Filter = NULL;
	}
	if (!Broker) {
		/*
		 *		Attempt to create a new broker
		 */
		MyBroker.nb_Descr = MSG(MSG_COMMODITY_DESC);
		MyBroker.nb_Port  = BrokerMP;
		MyBroker.nb_Pri   = CommodityPriority;
		Broker = CxBroker(&MyBroker, NULL);
		if (!Broker)
			return (0);
		ActivateCxObj(Broker, 1);
	}
	/*
	 *		Okay, now we create a new filter object to handle the
	 *		commodity string we were passed
	 */
	if (Filter)
		DeleteCxObjAll(Filter);
	
	Filter = CxFilter(hotkey);
	if (!Filter) {
		/*
		 *		Couldn't create the filter (probably out of memory --
		 *		specifying an illegal hotkey doesn't cause this to
		 *		fail, but instead produces an accumulated error later).
		 *
		 *		We still leave the broker itself installed, since it can
		 *		be used to allow the CX to send us Enable/Disable/Show/
		 *		Hide/Quit messages.
		 */
		return (0);
	}

	/*
	 *		Now add our various translation bits and pieces to the filter
	 */
	newobj = CxSignal(SysBase->ThisTask, SIGBREAKB_CTRL_F);
	if (!newobj)
		goto filter_error;
	AttachCxObj(Filter, newobj);

	newobj = CxTranslate(NULL);
	if (!newobj)
		goto filter_error;
	AttachCxObj(Filter, newobj);

	if (CxObjError(Filter))
		goto filter_error;

	AttachCxObj(Broker, Filter);
	HotKeyActive = 1;	/* Everything went okay so show we're active */
	return (1);

filter_error:
	DeleteCxObjAll(Filter);
	Filter = NULL;
	return (NULL);
}

/*
 *		HandleHotKey()
 *
 *		Handles any incoming messages on the commodities exchange port.
 *		Should be called whenever a CommodityMask signal is received.
 */
void HandleHotKeyMsgs(void)
{
	CxMsg *msg;

	if (!BrokerMP)
		return;

	while ((msg = (CxMsg *)GetMsg(BrokerMP)) != NULL) {
		ULONG msgid		= CxMsgID(msg);
		ULONG msgtype	= CxMsgType(msg);

		ReplyMsg((void *)msg);
		if (msgtype == CXM_COMMAND) {
			switch (msgid) {
				
				case CXCMD_DISABLE:
					SetMonitorMode(MONITOR_DISABLED);
					break;

				case CXCMD_ENABLE:
					if (Disabled)	/* Don't want to enable if Paused */
						SetMonitorMode(MONITOR_NORMAL);
					break;

				case CXCMD_KILL:
					QuitFlag = 1;
					break;

				case CXCMD_APPEAR:
				case CXCMD_UNIQUE:
					ShowSnoopDos();
					break;

				case CXCMD_DISAPPEAR:
					HideSnoopDos();
					break;
			}
		}
	}
}

/*
 *		CleanupHotKey()
 *
 *		Frees any resources used by the commodities module
 */
void CleanupHotKey(void)
{
	if (Broker) {
		DeleteCxObjAll(Broker);
		Broker = NULL;
		Filter = NULL;
	}
	if (BrokerMP) {
		struct Message *msg;
		while ((msg = GetMsg(BrokerMP)) != NULL)
			ReplyMsg(msg);
		DeleteMsgPort(BrokerMP);
		BrokerMP	  = NULL;
		CommodityMask = 0;
	}
	HotKeyActive = 0;
}

/*
 *		GetCommandName()
 *
 *		Returns a pointer to a static string containing the name of
 *		this program. The pointer remains valid until the next call
 *		to this function.
 */
char *GetCommandName(void)
{
	static char name[200];
	struct Process *pr = (struct Process *)SysBase->ThisTask;
	struct CommandLineInterface *cli = BTOC(pr->pr_CLI);
	char *cmdname = (char *)BTOC(cli->cli_CommandName);

	if (WBenchMsg)
		return (WBenchMsg->sm_ArgList->wa_Name);
	
	memcpy(name, cmdname+1, *cmdname);
	name[*cmdname] = '\0';
	return (name);
}
 
/*
 *		GetProgramIcon()
 *
 *		Returns a pointer to an icon structure containing a program
 *		icon for SnoopDos, as follows:
 *
 *			If SnoopDos was run from Workbench, we have a lock on it
 *			If SnoopDos was run from CLI, we look in the directory
 *			the program was run from. If we can't find it there, then
 *			we give up and use the default SnoopDos icon image.
 *
 *		Regardless, you should call CleanupIcons() before exiting,
 *		to free up any loose icon images that may have been allocated.
 *
 *		Warning: icon.library must be open when you call this function.
 */
struct DiskObject *GetProgramIcon(void)
{
	if (ProgramIcon)
		return (ProgramIcon);
	
	if (WBenchMsg) {
		/*
		 *		Running from Workbench, so try and get the program icon
		 *		indicated in the lock
		 */
		struct WBArg *wbarg = WBenchMsg->sm_ArgList;
		BPTR lk;

		lk          = CurrentDir(wbarg->wa_Lock);
		ProgramIcon = GetDiskObject(wbarg->wa_Name);
		CurrentDir(lk);
	} else {
		/*
		 *		Running from CLI so try and get icon associated with executable
		 */
		struct Process *pr = (struct Process *)SysBase->ThisTask;
		BPTR lk;

		if (pr->pr_HomeDir)	
			lk = CurrentDir(pr->pr_HomeDir);
		ProgramIcon = GetDiskObject(GetCommandName());
		if (pr->pr_HomeDir)
			CurrentDir(lk);
	}
	if (ProgramIcon)
		return (ProgramIcon);
	
	/*
	 *		Okay, unfortunately we couldn't locate the program icon, so
	 *		instead, let's just fall back to the default icon.
	 */
	DefSnoopDosIcon.do_StackSize = MINSTACKSIZE;	/* Fix this up here */
	return (&DefSnoopDosIcon);
}

/*
 *		CleanupIcons()
 *
 *		Frees any icon-related resources that were allocated
 */
void CleanupIcons(void)
{
	RemoveProgramFromWorkbench();
	if (ProgramIcon) {
		FreeDiskObject(ProgramIcon);
		ProgramIcon = NULL;
	}
	if (DefIconObj) {
		FreeDiskObject(DefIconObj);
		DefIconObj = NULL;
		AppIconObj = NULL;
	}
	if (WorkbenchPort) {
		struct Message *msg;

		while ((msg = GetMsg(WorkbenchPort)) != NULL)
			ReplyMsg(msg);

		DeleteMsgPort(WorkbenchPort),
		WorkbenchPort = NULL;
		WorkbenchMask = 0;
	}
	if (WorkbenchBase) {
		CloseLibrary(WorkbenchBase);
		WorkbenchBase = NULL;
	}
}

/*
 *		WriteIcon(filename)
 *
 *		Writes a SnoopDos project icon to the associated filename. If an
 *		icon already exists for the filename, then that icon is left alone.
 *
 *		If no icon exists, then we create one using a copy of what we think
 *		is the icon SnoopDos was started from (or the default).
 */
void WriteIcon(char *filename)
{
	struct DiskObject newobj;
	struct DiskObject *dobj;

	if (!IconBase)
		return;
	
	dobj = GetDiskObject(filename);
	if (dobj) {
		FreeDiskObject(dobj);
		return;
	}
	dobj = GetProgramIcon();

	/*
	 *		We can create a copy of our tool icon and change
	 *		it to a project icon.
	 */
	newobj = *dobj;
	newobj.do_CurrentX  	= NO_ICON_POSITION;
	newobj.do_CurrentY  	= NO_ICON_POSITION;
	newobj.do_ToolTypes 	= DefToolTypes;
	newobj.do_Type			= WBPROJECT;
	if (WBenchMsg)
		newobj.do_DefaultTool = WBenchMsg->sm_ArgList->wa_Name;
	else
		newobj.do_DefaultTool = GetCommandName();
	PutDiskObject(filename, &newobj);
}

/*
 *		AddProgramToWorkbench(hidetype)
 *
 *		Creates an AppIcon or AppMenuItem which can be used to re-active
 *		SnoopDos, and attaches it to Workbench. Normally used when
 *		SnoopDos goes into a HIDE state.
 *
 *		Hidetype is HIDE_ICON or HIDE_TOOLS, depending on which type of
 *		hide method is required.
 *
 *		Initialises WorkbenchMask accordingly.
 *
 *		Call RemoveProgramFromWorkbench() to remove the item later on.
 *
 *		Returns TRUE for success, FALSE for failure.
 */
int AddProgramToWorkbench(int hidetype)
{
	if (!IconBase)
		return (FALSE);

	if (!WorkbenchBase) {
		WorkbenchBase = OpenLibrary("workbench.library", 37);
		if (!WorkbenchBase)
			return (FALSE);
	}
	if (!WorkbenchPort) {
		WorkbenchPort = CreateMsgPort();
		if (!WorkbenchPort)
			return (FALSE);
		WorkbenchMask = (1 << WorkbenchPort->mp_SigBit);
	}
	if (hidetype == HIDE_TOOLS && !MyAppMenu) {
		/*
		 *		Adding an item to the Workbench tools menu
		 */
		MyAppMenu = AddAppMenuItem(0, 0, MSG(MSG_APPMENU_NAME),
								   WorkbenchPort, NULL);
		if (!MyAppMenu)
			return (FALSE);
	}
	if (hidetype == HIDE_ICON && !MyAppIcon) {
		/*
		 *		Adding an AppIcon to the Workbench. We make a copy
		 *		of the current program icon to use as our appicon.
		 */
		struct DiskObject *dobj;

		if (!AppIconObj) {
			/*
			 *		We need a temporary icon to play around with.
			 *		We allocate a new default disk object, copy
			 *		it, and modify our copy to give us an icon
			 *		we can use as an AppIcon. We need to remember
			 *		to free the original icon before we exit.
			 */
			static struct DiskObject defobj;
				
			DefIconObj = GetDefDiskObject(WBTOOL);
			if (!DefIconObj)
				return (FALSE);

			defobj     = *DefIconObj;
			AppIconObj = &defobj;
			dobj       = GetProgramIcon();

			AppIconObj->do_Gadget.Width			= dobj->do_Gadget.Width;
			AppIconObj->do_Gadget.Height		= dobj->do_Gadget.Height;
			AppIconObj->do_Gadget.Flags			= dobj->do_Gadget.Flags;
			AppIconObj->do_Gadget.GadgetRender	= dobj->do_Gadget.GadgetRender;
			AppIconObj->do_Gadget.SelectRender	= dobj->do_Gadget.SelectRender;
			AppIconObj->do_Type   				= 0;
		}
		AppIconObj->do_CurrentX	= CurSettings.IconPosLeft != -1 ?
								  CurSettings.IconPosLeft :
								  NO_ICON_POSITION;
		AppIconObj->do_CurrentY	= CurSettings.IconPosTop != -1 ?
								  CurSettings.IconPosTop :
								  NO_ICON_POSITION;
		MyAppIcon = AddAppIcon(0, 0, APPICON_NAME, WorkbenchPort,
							   NULL, AppIconObj, TAG_DONE);
		if (!MyAppIcon)
			return (FALSE);
	}
	return (TRUE);
}

/*
 *		RemoveProgramFromWorkbench()
 *
 *		Removes any AppIcon or AppMenuItem item that is currently installed
 *		on Workbench. Usually called when the SnoopDos window is about
 *		about to reappear.
 */
void RemoveProgramFromWorkbench(void)
{
	if (MyAppMenu)		RemoveAppMenuItem(MyAppMenu),	MyAppMenu = NULL;
	if (MyAppIcon)		RemoveAppIcon(MyAppIcon),		MyAppIcon = NULL;
}

/*
 *		HandleWorkbenchMsgs()
 *
 *		Handles any outstanding messages at the Workbench message port
 */
void HandleWorkbenchMsgs(void)
{
	struct AppMessage *msg;
	int show = 0;

	if (!WorkbenchPort)
		return;
	
	while ((msg = (void *)GetMsg(WorkbenchPort)) != NULL) {
		show = 1;
		ReplyMsg(msg);
	}
	if (show)
		ShowSnoopDos();
}
