/*
 * Yak version 1.56
 * ----------------
 * [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"

#define CATCOMP_BLOCK
#define CATCOMP_NUMBERS
#include "yak_locale_strings.h"
#undef CATCOMP_BLOCK

#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 Locale *locale;
struct Catalog *Catalog;
struct MsgPort *broker_mp;
CxObj *broker;
char *PopKeyStr;
#define POPKEY_EVENT	1L	/* cannot clash with YHK event... */

static const char *versionstr=VERSION_STR;

struct NewBroker newbroker = {
    NB_VERSION,
    "Yak",						/* string to identify this broker */
    VERSION_BROKER,
    "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)
	{
		CloseCatalog (Catalog); /* NULL is valid */
		CloseLocale(locale);
		CloseLibrary(LocaleBase);
	}
}

/* open libraries, devices that we need */
static BOOL
OpenResources(void)
{
	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)))
	{
		return TRUE;
	}
	CloseResources();
	return FALSE;
}


/* open locale library and catalog */
void
OpenLocaleStuff(char *language)
{

	if (LocaleBase =(struct LocaleBase *)OpenLibrary("locale.library", 38L))
	{
		if (!(locale = OpenLocale(NULL)))
		{	
			PostError("No locale set!");
			CloseLibrary(LocaleBase);
			LocaleBase = NULL;
		}
		Catalog = OpenCatalog( 	locale, "yak.catalog", 
							  OC_BuiltInLanguage, "english", 
							  OC_Language, language,        
							  OC_Version, 15L, 
							  TAG_DONE );
	}	
}


/* slighlty modified version of GetString() generated by catcomp */

char *getString(ULONG MsgID)
{
LONG   *l;
UWORD  *w;
char *builtIn;

   l = (LONG *)CatCompBlock;

   while (*l != MsgID )
   {
	   w = (UWORD *)((ULONG)l + 4);
	   l = (LONG *)((ULONG)l + (ULONG)*w + 6);
   }
   builtIn = (char *)((ULONG)l + 6);
   if (LocaleBase)
	   return (GetCatalogStr (Catalog, MsgID, builtIn));
   return(builtIn);
}

/* 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 = getString(Error_Requester_Title);
	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();
					
					/* Open the right locale if tooltype LANGUAGE is used */
					OpenLocaleStuff(TTString("LANGUAGE",NULL));

					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();
		MWReport("At end of main()", MWR_FULL);  /* Generate a memory usage report */
	}
	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);
}




