/*
 * Yak version 1.0
 * ---------------
 * [Yak == Yet Another K(?)ommodity
 *
 * There seems to be a profusion of commodities doing this or that.
 * Heres mine, to do what I want it to:
 *
 *	AutoActivate windows (SunMouse)
 *	ClickToFront, ClickToBack, ScreenCycle
 *	Close/Zip/Shrink/Zoom a window via keyboard.
 *	Bring up a palette on front screen.
 *	Insert date into read-stream.
 *	Produce key-click (like my keyclick program).
 *	Some other things...
 *
 * Martin W. Scott, 9/92.
 */
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#include <dos/dostags.h>
#include <libraries/commodities.h>
#include <libraries/reqtools.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/commodities.h>
#include <proto/intuition.h>
#include <proto/reqtools.h>
#include <clib/alib_protos.h>
#include <string.h>

#include "yak.h"
#include "beep.h"


struct Library *CxBase, *IconBase, *GadToolsBase, *LayersBase;
struct ReqToolsBase *ReqToolsBase;
struct IntuitionBase *IntuitionBase;
struct MsgPort *broker_mp;
CxObj *broker;

struct NewBroker newbroker = {
    NB_VERSION,
    "Yak",           /* string to identify this broker */
    "Yak 1.0  © 1992, Martin W. Scott",
    "Multi-purpose commodity",
    NBU_UNIQUE | NBU_NOTIFY,      /* Don't want any new commodities
                                   * starting with this name.  If someone
                                   * tries it, let me know */
    COF_SHOW_HIDE
};

ULONG		cxsigflag;
extern ULONG	clicksigflag;
extern struct WBStartup *WBenchMsg;


/* close what we opened */
void
CloseResources()
{
	if (IntuitionBase) CloseLibrary(IntuitionBase);
	if (CxBase) CloseLibrary(CxBase);
	if (LayersBase) CloseLibrary(LayersBase);
	if (IconBase) CloseLibrary(IconBase);
	if (GadToolsBase) CloseLibrary(GadToolsBase);
}

/* open libraries, devices that we need */
BOOL
OpenResources()
{
	if ((IntuitionBase = (void *)OpenLibrary("intuition.library", 37L)) &&
	    (CxBase = OpenLibrary("commodities.library", 37L)) &&
	    (LayersBase = OpenLibrary("layers.library", 37L)) &&
	    (IconBase = OpenLibrary("icon.library", 37L)) &&
	    (GadToolsBase = OpenLibrary("gadtools.library", 37L)))
        {
		return TRUE;
	}
	CloseResources();
	return FALSE;
}

/* pop up a requester */
void
EasyEasyRequest(char *str)
{
	struct EasyStruct es;

	es.es_StructSize = sizeof(struct EasyStruct);
	es.es_Flags = 0L;
	es.es_Title = "Yak: ";
	es.es_TextFormat = str;
	es.es_GadgetFormat = "OK";
	EasyRequestArgs(NULL, &es, NULL, NULL);
}

/* display an error appropriately */
void
PostError(char *str)
{
	if (IntuitionBase && WBenchMsg)
		EasyEasyRequest(str);
	else
	{
		PutStr("Yak: ");
		PutStr(str);
	}
}

/* modify hk array with new hotkey */	
BOOL
AddHotKey(char *newstr, UWORD n)
{
	CxObj *newfilter;

	if (newfilter = HotKey(newstr, broker_mp, hk[n].msgid))
	{
		if (hk[n].filter) DeleteCxObjAll(hk[n].filter);
		hk[n].filter = newfilter;
		strncpy(hk[n].key, newstr, MAXKEYLEN);
		AttachCxObj(broker, hk[n].filter);
		return TRUE;
	}
	return FALSE;
}

/* simple extension to ArgXXX routines */
BOOL
ArgBool(char **ttypes, char *tt, BOOL def)
{
	char	*s;

	s = ArgString(ttypes, tt, def ? "YES" : "NO");

	return	((strcmp(s, "YES") == 0) ||
		(strcmp(s, "TRUE") == 0) ||
		(strcmp(s, "ON") == 0)) ? TRUE : FALSE;
}


void
main(int argc, char **argv)	/* Yak: multi-function commodity */
{
	char	**ttypes;

	if (OpenResources())
	{
	    if (broker_mp = CreateMsgPort())
            {
                newbroker.nb_Port = broker_mp;
                cxsigflag = 1L << broker_mp->mp_SigBit;

		/* process tool-types */
                ttypes = ArgArrayInit(argc, argv);

                newbroker.nb_Pri = (BYTE)ArgInt(ttypes, "CX_PRIORITY", 0);

		if (broker = CxBroker(&newbroker, NULL))
                {
			if (InitSettings(ttypes))
			{
				if (InitHandler())
				{
					MyPri(ACTIVE);
					ActivateCxObj(broker, 1L);

					if (ArgBool(ttypes, "CX_POPUP", FALSE))
						ShowWindow();

					while (ProcessMsg())
						;
					HideWindow();
					EndHandler();
					MyPri(ORIGINAL);
				}
				else PostError("Allocation error\n");
			}
			else PostError("HotKey definition error\n");

                    DeleteCxObjAll(broker);
                }

                DeletePort(broker_mp);
		ArgArrayDone();
            }
	    else PostError("Allocation error\n");

	    CloseResources();
	}
	else PostError("Resource error\n");
}

/* simulate user clicking close-gadget */
void
DoCloseWindow()
{
	struct InputEvent ev;
	struct Window *window;
	ULONG lock;

	lock = LockIBase(0);
	window = IntuitionBase->ActiveWindow;
	UnlockIBase(lock);

	if (window && (window->IDCMPFlags & CLOSEWINDOW))
	{
		ev.ie_NextEvent = NULL;
		ev.ie_Class = IECLASS_CLOSEWINDOW;
		ev.ie_SubClass = 0;
		ev.ie_Code = 0;
		ev.ie_Qualifier = 0;
		ev.ie_EventAddress = 0;
		AddIEvents(&ev);
	}
}

#define AdjustedLeftEdge(w, width)	(w->LeftEdge + width > w->WScreen->Width ? \
						w->WScreen->Width - width : w->LeftEdge)
#define AdjustedTopEdge(w, height)	(w->TopEdge + height > w->WScreen->Height ? \
						w->WScreen->Height - height : w->TopEdge)

/* zip/enlarge or whatever the currently active window */
void
HandleWindowKey(LONG what)
{
	struct Window *window;
	ULONG lock;

	lock = LockIBase(0);
	if (window = IntuitionBase->ActiveWindow)
	{
		switch (what)
		{
		case HK_ZIPKEY:
			if (window->Flags & WFLG_HASZOOM)
				ZipWindow(window);
			break;

		case HK_SHRINKKEY:
			if (window->Flags & WFLG_SIZEGADGET)
				ChangeWindowBox(window,
						window->LeftEdge,
						window->TopEdge,
						window->MinWidth,
						window->MinHeight);
			break;

		case HK_ZOOMKEY:
		{	/* sometimes Max vars are -1 == NO LIMIT */
			USHORT width, height;

			width = window->MaxWidth;
			if (width == -1) width = window->WScreen->Width-width;
			height = window->MaxHeight;
			if (height == -1) height = window->WScreen->Height-height;
			
			if (window->Flags & WFLG_SIZEGADGET)
				ChangeWindowBox(window,
						AdjustedLeftEdge(window, width),
						AdjustedTopEdge(window, height),
						width,
						height);
		}

		} /* switch */
	}
	UnlockIBase(lock);
}

/* write chars in str to input-stream */
void
WriteEvents(char *str)
{
	struct InputEvent ev;

	ev.ie_NextEvent = NULL;
	for (; *str; str++)
	{
		InvertKeyMap((ULONG)*str, &ev, NULL);
		AddIEvents(&ev);
	}
}

/* bring a palette up on frontmost screen */
void
DoPalette()
{
	if (ReqToolsBase = (void *)OpenLibrary("reqtools.library", 0L))
	{
		(void) rtPaletteRequest("Palette", NULL,
			RT_Screen, IntuitionBase->FirstScreen, TAG_DONE);

		CloseLibrary(ReqToolsBase);
	}
}

/* insert date as string into read-stream */
void
InsertDate()
{
	char day[LEN_DATSTRING], date[LEN_DATSTRING], time[LEN_DATSTRING];
	struct DateTime dt;

	DateStamp(&dt.dat_Stamp);
	dt.dat_Format = datefmt;
	dt.dat_Flags = 0;
	dt.dat_StrDay = day;
	dt.dat_StrDate = date;
	dt.dat_StrTime = time;
	DateToStr(&dt);

	if (insday) WriteEvents(day);
	if (insdate) {
		if (insday) WriteEvents(" ");
		WriteEvents(date);
	}
	if (instime) {
		if (insday | insdate) WriteEvents(" ");
		WriteEvents(time);
	}
}

/* monitor cx port, act on messages */
LONG
ProcessMsg(void)
{
    CxMsg *msg;
    ULONG sigrcvd, msgid, msgtype;
    LONG returnvalue = 1L;

    sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag | clicksigflag | wndsigflag);

    if (sigrcvd & clicksigflag)		/* keyclick please */
    {
	beep(click_volume);
	Delay(1);   /* avoid ugly sound when key repeating */
    }

    if (sigrcvd & wndsigflag)		/* settings change */
    	if (HandleIDCMP() != HELP_OKAY)
		returnvalue = 0;

    while(msg = (CxMsg *)GetMsg(broker_mp))
    {
        msgid = CxMsgID(msg);
        msgtype = CxMsgType(msg);
        ReplyMsg((struct Message *)msg);

        switch(msgtype)
        {
            case CXM_IEVENT:
                switch(msgid)
                {
		    case HK_POPKEY:
			ShowWindow();	/* check error return? */
			break;

		    case HK_CLOSEKEY:
			DoCloseWindow();
			break;

		    case HK_ZIPKEY:
		    case HK_SHRINKKEY:
		    case HK_ZOOMKEY:
			HandleWindowKey(msgid);
                        break;

		    case HK_WORKBENCH:
			{
			    struct Screen *s = LockPubScreen("Workbench");
			    if (s)
			    {
				struct Window *w = s->FirstWindow;
				WBenchToFront();
				Forbid();
				for (; w; w = w->NextWindow)
					if (w->Flags & WFLG_WBENCHWINDOW)
					{
						ActivateWindow(w);
						break;
					}
				Permit();
				UnlockPubScreen(NULL, s);
			    }
			}
			break;
				
		    case HK_POPPALKEY:
			DoPalette();
			break;

		    case HK_INSDATE:
			InsertDate();
			break;

		    case HK_POPCLI:
			{
			    BPTR nilfh = Open("NIL:", MODE_NEWFILE);
			    struct Screen *s = LockPubScreen(NULL);
			    if (s)
			    {
				ScreenToFront(s);
				UnlockPubScreen(NULL, s);
			    }
			    Execute(PopCommand, NULL, nilfh);
			    if (nilfh)
				Close(nilfh);
			}
			break;			
                }
                break;

            case CXM_COMMAND:
                switch(msgid)
                {
                    case CXCMD_UNIQUE:
		    case CXCMD_APPEAR:
			ShowWindow();	/* check error return? */
			break;

		    case CXCMD_DISAPPEAR:
			HideWindow();
			break;

                    case CXCMD_DISABLE:
                        ActivateCxObj(broker, 0L);
                        break;

                    case CXCMD_ENABLE:
                        ActivateCxObj(broker, 1L);
                        break;

                    case CXCMD_KILL:
                        returnvalue = 0L;
                        break;
                }
                break;
        }
    }

    if (sigrcvd & SIGBREAKF_CTRL_C)
        returnvalue = 0L;

    return(returnvalue);
}
