/*
 * Yak version 1.53
 * ----------------
 * [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/Turn 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 & Gaël Marziou, 8/93.
 */
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <graphics/displayinfo.h>
#include <libraries/commodities.h>
#include <libraries/reqtools.h>
#include <libraries/locale.h>
#include <intuition/intuitionbase.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/commodities.h>
#include <proto/intuition.h>
#include <proto/reqtools.h>
#include <proto/locale.h>
#include <string.h>
#include <stdarg.h>

#include "yak.h"
#include "hotkey_types.h"
#include "beep.h"
#include "icon.h"
#include "version.h"
#ifndef YAK_LOCALE_STRINGS
#include "yak_locale_strings.h"
#endif

#include "WB2CLI.h"	/* we'll be a shell process */
#define DEF_CURRENTDIR	"SYS:"

/* local prototypes for main.c */
static void CloseResources(void);
static BOOL OpenResources(void);
static void FreePatterns(void);
static LONG ProcessMsg(void);
void __main(void);

#ifdef __SASC_60	/* save some bytes */
void _MemCleanup(void){}
#else
void MemCleanup(void){}
#endif

/* global data - library bases and the like */
extern struct WBStartup *_WBenchMsg;
struct Library	*CxBase, *IconBase,
		*GadToolsBase, *LayersBase,
		*WorkbenchBase, *LocaleBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct DosLibrary *DOSBase;
struct Locale *locale;
struct Catalog *Catalog;
struct MsgPort *broker_mp;
CxObj *broker;
char *PopKeyStr;
#define POPKEY_EVENT	1L	/* cannot clash with YHK event... */

char *versionstr="\0$VER: "VERSION_STR;

struct NewBroker newbroker = {
    NB_VERSION,
    "Yak",           /* string to identify this broker */
    VERSION_STR,
    "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		wndsigflag;		/* here for overlay purposes */
ULONG		cxsigflag;
ULONG		invariantsigflag;
BYTE		palette_count;		/* how many palettes are open */

/* from handler.c */
extern ULONG	clicksigflag, intuiopsigflag;
extern void (*intui_routine)(APTR);	/* for intui_op's */
extern APTR intui_parameter;

/* from icon.c */
extern ULONG	appsigflag;

/* close what we opened */
static void
CloseResources()
{
	if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	if (GfxBase) CloseLibrary((struct Library *)GfxBase);
	if (CxBase) CloseLibrary(CxBase);
	if (LayersBase) CloseLibrary(LayersBase);
	if (IconBase) CloseLibrary(IconBase);
	if (GadToolsBase) CloseLibrary(GadToolsBase);
	if (WorkbenchBase) CloseLibrary(WorkbenchBase);
	if (LocaleBase)
	{
		if (Catalog) CloseCatalog (Catalog);
		CloseLocale(locale);
		CloseLibrary(LocaleBase);
	}
}

/* open libraries, devices that we need */
static BOOL
OpenResources()
{
	if ((IntuitionBase = (void *)OpenLibrary("intuition.library", 37L)) &&
	    (GfxBase = (void *)OpenLibrary("graphics.library", 37L)) &&
	    (CxBase = OpenLibrary("commodities.library", 37L)) &&
	    (LayersBase = OpenLibrary("layers.library", 37L)) &&
	    (IconBase = OpenLibrary("icon.library", 37L)) &&
	    (GadToolsBase = OpenLibrary("gadtools.library", 37L)) &&
	    (WorkbenchBase = OpenLibrary("workbench.library", 37L)) &&
	    (DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37L)))
        {
		if (LocaleBase =(struct LocaleBase *)OpenLibrary("locale.library", 38L))
			if (!(locale = OpenLocale(NULL)))
			{
				PostError("No locale set!");
				CloseLibrary(LocaleBase);
				LocaleBase = NULL;
			}
		    else
				Catalog = OpenCatalog( NULL, "yak.catalog", 
										  OC_BuiltInLanguage, "english", 
										  OC_Version, 15L, 
										  TAG_DONE );
		return TRUE;
	}
	CloseResources();
	return FALSE;
}

/* simple requester with args */
void
PostError(char *body, ... )
{
	struct EasyStruct es;
	va_list args;

	if (!IntuitionBase)
	{
		Write(Output(), "Need AmigaDos 2.0+\n", -1);
		return;
	}

	/* setup the argument array */
	va_start( args, body );

	/* initialise the structure */
	es.es_StructSize = sizeof(struct EasyStruct);
	es.es_Flags = 0L;
	es.es_Title = "Yak Message";
	es.es_TextFormat = body;
	es.es_GadgetFormat = "OK";

	/* display the requester */
	EasyRequestArgs(NULL, &es, NULL, args);

	/* free the arguments */
	va_end( args );
}

/* parse pattern, report errors */
BOOL __regargs
InitPattern(char *newpatstr, UWORD n)
{
	char *patstr = newpatstr ? newpatstr : patterns[n].patstr;
	char *pat;
	LONG len;

	if (pat = AllocVec(len = strlen(patstr)*3+10, MEMF_CLEAR))
	{
		if (ParsePattern(patstr, pat, len) != -1)
		{
			if (newpatstr) strncpy(patterns[n].patstr, newpatstr, PATLEN);
			if (patterns[n].pat) FreeVec(patterns[n].pat);
			patterns[n].pat = pat;
			return TRUE;
		}
		
		PostError("%s:\n\"%s\"", getString(Parsing_Pattern_ERR), patstr);
		FreeVec(pat);
	}
	else PostError(getString(Allocation_ERR));
	return FALSE;
}

static void
FreePatterns()
{
	UWORD i;

	for (i = 0; i < NUM_PATTERNS; i++)
		if (patterns[i].pat) FreeVec(patterns[i].pat);
}

void
__main()		/* Yak: multi-function commodity */
{
	BPTR	newdir = NULL, olddir;

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

		/* process tool-types */
		GetOurIcon(_WBenchMsg);
                newbroker.nb_Pri = (BYTE)TTInt("CX_PRIORITY", 0);

		if (_WBenchMsg)
		{
			if (newdir = Lock(DEF_CURRENTDIR, ACCESS_READ))
				olddir = CurrentDir(newdir);
			WB2CLI(_WBenchMsg,4000,DOSBase);		/* get it over with... */
		}

		if (broker = CxBroker(&newbroker, NULL))
                {
			/* HANDLER FIRST, SO IT SEES EVERYTHING!!! */
			if (InitHandler())
			{
				InitYakHotKeyList();
				LoadSettings();

				if (PopKeyStr = DupStr(TTString("CX_POPKEY", "Rcommand help")))
				{
					CxObj *tmpobj;

					if (tmpobj = HotKey(PopKeyStr, broker_mp, POPKEY_EVENT))
						AttachCxObj(broker, tmpobj);
					else
						PostError("%s:\"%s\"", getString(CX_POPKEY_invalid_ERR), 
								  PopKeyStr);
				}
				/* else... if this failed, we lose */

				MyPri(ACTIVE);
				ActivateCxObj(broker, 1L);

				if (TTBool("CX_POPUP", FALSE))
					ShowWindow();

				if (TTBool("APPICON", FALSE))
				{
					if (!MakeOurAppIcon(TTString("ICONNAME", "Yak!")))
						if (_WBenchMsg)
							PostError(getString(Couldn_t_create_AppIcon_ERR));
				}
				/* WB makes a copy of icon, so can free it */
				FreeOurIcon();

				/* these are the signals waited for, + window sig */
				invariantsigflag = SIGBREAKF_CTRL_C | cxsigflag
						   | clicksigflag | intuiopsigflag
						   | appsigflag;

				while (ProcessMsg())
					;
				HideWindow();
				RemoveOurAppIcon();
				DeleteYakHotKeyList();
				FreeStr(PopKeyStr);
				MyPri(ORIGINAL);

				EndHandler();
				FreePatterns();
			}
			else PostError(getString(Allocation_ERR));

                    DeleteCxObjAll(broker);
                }

		if (newdir) {
			CurrentDir(olddir);
			UnLock(newdir);
		}

                DeleteMsgPort(broker_mp);
		FreeOurIcon();	/* may already be gone, but so what? */
            }
	    else PostError(getString(Allocation_ERR));

	    CloseResources();
	}
	else PostError(getString(Resource_ERR));
}

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

    sigrcvd = Wait(invariantsigflag | wndsigflag);

    if (sigrcvd & intuiopsigflag)		/* intuiop requested */
	intui_routine(intui_parameter);

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

    if (sigrcvd & appsigflag)		/* settings change */
    {
	RespondToAppIcon();
	ShowWindow();
    }

    if (sigrcvd & wndsigflag)		/* settings change */
    	if (HandleIDCMP() != HELP_OKAY)
		returnvalue = 0;
	else if (!wndsigflag)		/* window gone */
		DummyOverlay();		/* remove code */

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

        switch(msgtype)
        {
            case CXM_IEVENT:

		if (msgid == POPKEY_EVENT)
			ShowWindow();
		else
			/* a generic hotkey... */
			PerformAction((YakHotKey *)msgid);
		break;

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

		    case CXCMD_DISAPPEAR:
			HideWindow();
			DummyOverlay();
			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;

    if (!returnvalue && !OkayToExit())
    {
	PostError(getString(Cannot_exit_palette_opened_ERR));
	returnvalue = 1;
    }
    
    return(returnvalue);
}
