/*
 *	ParM.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	01 Jul 1990
 *	Modified:	28 Jul 1991  18:25:29
 *
 *	Make>> make
 */

#include "ParMBase.h"


#define COPYRIGHT "ParM V3.0 Copyright © 1990-91 by S.R. & P.C."
#define DEFAULT_CONFIG_FILE "S:ParM.cfg"

#define THE_END			0
#define NO_WIN			10

#define OPEN_ITEM		0
#define UPDATE_ITEM		1
#define STD_CFG_ITEM	2
#define CMDMODE_ITEM	3
#define COMMAND_ITEM	4
#define CHDIR_ITEM		5
#define QUIT_ITEM		6

/* ParM modes */
#define MODE_CLI		0	/* default mode */
#define MODE_WINDOW		1
#define MODE_MYMENU		2


extern void CreateParMenu(short Color);
extern void exit(int);


/*****				global variables					*****/

extern struct ExecBase *SysBase;
extern struct ParMBase *ParMBase;
extern struct Menu Menu1;
extern struct MenuItem SubItem1;
extern struct WBStartup *WBenchMsg;


/*****				 local variables					*****/

static struct Process *ParMProcess;
static struct Window *Win;
static struct Menu *LastWBMenu, *TheMenu;
static struct MsgPort SwapPort;
static short FirstParMMenuNum;		/* = 0  if not attached to WorkBench */
static short ParMMode;
static ULONG OldIDCMP, WinSignal;
static BOOL PortTaken;
static BOOL quit;
static BYTE ParMenuColor = -1;
static struct FileReq FileReq;
static char Dir[REQ_DSIZE+1];
static char File[REQ_FCHARS+1];
static char Path[REQ_DSIZE+REQ_FCHARS+2];
static struct ParMConfig ParMConfig;
static BOOL DoNextSelect;


struct ToolTypesArg {
	char *tta_Name;
	char tta_cli_equ;
};

static struct ToolTypesArg TTA_List[] = {
	{"STACKSIZE",	's'},
	{"LEFTEDGE",	'x'},
	{"TOPEDGE",		'y'},
	{"DETAILPEN",	'd'},
	{"BLOCKPEN",	'b'},
	{"MENUCOLOR",	'c'},
	{"CONFIGFILE",	'f'},
	{"NODRAGBAR",	'r'},
	{"DEPTHGADGETS",'g'},
	{"NOAUTOFRONT",	'a'},
	{"MYMENU",		'm'}
};

#define TTANUM	11		/* Number of ToolTypesArg in List */


static struct NewWindow NWS = {
	0, 0,	/* window XY origin relative to TopLeft of screen */
	39, 10,	/* window width and height */
	3, 2,	/* detail and block pens */
	MENUPICK|RAWKEY|REFRESHWINDOW,	/* IDCMP flags */
	WINDOWDRAG|SIMPLE_REFRESH,	/* other window flags */
	NULL,	/* first gadget in gadget list */
	NULL,	/* custom CHECKMARK imagery */
	(UBYTE *)"ParM",	/* window title */
	NULL,	/* custom screen pointer */
	NULL,	/* custom bitmap */
	5, 5,	/* minimum width and height */
	-1, -1,	/* maximum width and height */
	WBENCHSCREEN	/* destination screen type */
};


static struct Window *FindWorkBench(void)
{
	struct Screen *WBS;
	struct Window *WBW = NULL;
	ULONG ilock;

	if (WBS = OpenWorkBench()) {
		ilock = LockIBase(0L);	/* Just so's things don't change on us */
		WBW = WBS->FirstWindow;
		while (WBW) {
			/* Only main WB Window has NEWPREFS IDCMPFlag set, so find it */
			if ((WBW->Flags & WBENCHWINDOW) && (WBW->IDCMPFlags & NEWPREFS)) {
				TheMenu = WBW->MenuStrip;
				LastWBMenu = TheMenu->NextMenu->NextMenu;
				FirstParMMenuNum = 3;
				break;
			}
			WBW = WBW->NextWindow;
		}
		UnlockIBase(ilock);
	}
	return WBW;
}


void TakeMsgPort(void)
{
	struct MsgPort *WinPort;
	struct List *myMsgList = &(SwapPort.mp_MsgList);
	struct List *winMsgList;

	SwapPort.mp_Node.ln_Type = NT_MSGPORT;
	SwapPort.mp_Flags = PA_SIGNAL;
	Forbid();

	/* get the info we need */
	WinPort = Win->UserPort;
	winMsgList = &(WinPort->mp_MsgList);

	/* setup our port */
	SwapPort.mp_SigBit = WinPort->mp_SigBit;
	SwapPort.mp_SigTask = WinPort->mp_SigTask;

	/* flip things around */
	WinPort->mp_SigTask = (struct Task *)ParMProcess;
	/* Prevent signal for being allocated later */
	WinSignal = AllocSignal(WinPort->mp_SigBit);
	myMsgList->lh_Head = (struct Node *) &winMsgList->lh_Tail;
	myMsgList->lh_TailPred = winMsgList->lh_TailPred;
	winMsgList->lh_TailPred = (struct Node *) &myMsgList->lh_Head;
	myMsgList->lh_TailPred->ln_Succ = (struct Node *) &myMsgList->lh_Tail;

	Permit();

	/* prevent deadlocks */
	SetTaskPri((struct Task *)ParMProcess, SwapPort.mp_SigTask->tc_Node.ln_Pri + 1);
}


/* Restore things the way they were before TakeMsgPort() */

void DelMsgPort(void)
{
	struct Message *Msg;
	struct List *myMsgList = &(SwapPort.mp_MsgList);

	Forbid();
	/* clean out our list */
	while (Msg = GetMsg(&SwapPort))
		PutMsg(&SwapPort, Msg);
	/* restore things */
	myMsgList->lh_Head->ln_Pred = myMsgList->lh_TailPred;
	myMsgList->lh_TailPred->ln_Succ = myMsgList->lh_Head;
	Win->UserPort->mp_SigTask = SwapPort.mp_SigTask;
	if (WinSignal)
		FreeSignal(WinSignal);
	Permit();
}


/* Configuration managing functions  */

static void InstallWBMenus(struct Menu *Menus)
{
	long ilock;

	ilock = LockIBase(0);
	ClearMenuStrip(Win);
	if (Menus)
		CleanUp(TheMenu, 0, ParMConfig.ItemHeight);
	LastWBMenu->NextMenu = Menus;
	SetMenuStrip(Win, TheMenu);
	UnlockIBase(ilock);
}


static void ClearMenus(void)
{
	if (ParMMode == MODE_MYMENU) {
		InstallWBMenus(NULL);
		Menu1.LeftEdge = LastWBMenu->LeftEdge + LastWBMenu->Width;
	}
	else
		ClearMenuStrip(Win);
}


void UpDateMenus(void)
{
	ClearMenus();
	ParMConfig.MenuPen = ParMenuColor;
	if (!ParseMenus(&ParMConfig))
		FreeMenus(&ParMConfig);
	if (ParMMode == MODE_MYMENU)
		InstallWBMenus(&Menu1);
	else
		SetMenuStrip(Win, &Menu1);
}


static void OpenMenus(void)
{
	char *yuck;

	FileReq.Title = "Open";
	strcpy(Dir, ParMConfig.CurCfg);
	strcpy(File, yuck = BaseName(Dir));
	*yuck = '\0';		/* cut off filename from fr_Dir */
	if (!FileRequester(&FileReq))
		return;	/* quit if Open canceled */
	strcpy(ParMConfig.CurCfg, Path);
	UpDateMenus();
}


/* Print busy message in place of window title */

static void Busy(BOOL status)
{
	SetWindowTitles(Win, (status) ? "Busy" : "ParM", COPYRIGHT);
}


static void Usage(void)
{
	Printf("%s\nUsage: ParM [-m] [-w] [-x#] [-y#] [-d#] [-b#] [-c#] [-r] [-g] [-a] [-o] [-s#] [-fConfigFile]\n", COPYRIGHT);
	exit(0);
}


/* Atcho, bonsoir...  */

void Bye(short err)
{
	if (err == THE_END) {
		ClearMenus();
		FreeMenus(&ParMConfig);
		if (PortTaken)
			DelMsgPort();
		if (ParMMode == MODE_CLI)
			ModifyIDCMP(Win, OldIDCMP);
		else if (ParMMode == MODE_WINDOW)
			CloseWindowSafely(Win, NULL);
	}
	exit(err);
}


/* change parm current directory */

static void ChangeDir(char *dir)
{
	BPTR NewLock, OldLock;

	NewLock = Lock(dir, ACCESS_READ);
	if (NewLock != NULL) {
		OldLock = CurrentDir(NewLock);
		if (OldLock != NULL)
			UnLock(OldLock);
	}
}


static void DoExtMenu(USHORT MenuNum)
{
	struct Extended_MenuItem *Item;
	struct Extended_WBStartup *EWBS;

	Item = (struct Extended_MenuItem *)ItemAddress(TheMenu, MenuNum);
	switch (Item->emi_Mode) {
	case TOK_ARUN:
		ARun(&ParMConfig, &Item->emi_RunInfo);
		break;
	case TOK_RUN:
	case TOK_SHELL:
		Run(&ParMConfig, &Item->emi_RunInfo, Item->emi_Mode);
		break;
	case TOK_WB:
		if (EWBS = MakeWBStartup(&ParMConfig, &Item->emi_RunInfo))
			WBRun(&ParMConfig, EWBS, Item->emi_RunInfo.ri_Pri);
		break;
	case TOK_CFG:	/* new cfg */
		strcpy(ParMConfig.CurCfg, Item->emi_RunInfo.ri_Cmd);
		UpDateMenus();
		DoNextSelect = FALSE;	/* Tell DoIntuiMsg not to execute next menu selection */
	}
}


static void parse_arg(char opt, char *args)
{
	BPTR fh;
	long argl;

	argl = Atol(args);
	switch (opt) {
	case 'm':
		ParMMode = MODE_MYMENU;
		break;
	case 'w':
		ParMMode = MODE_WINDOW;
		break;
	case 's':
		if (argl >= 4000) ParMConfig.DefaultStack = argl;
		break;
	case 'x':
		NWS.LeftEdge = argl;
		ParMMode = MODE_WINDOW;
		break;
	case 'y':
		NWS.TopEdge = argl;
		ParMMode = MODE_WINDOW;
		break;
	case 'd':
		NWS.DetailPen = argl;
		ParMMode = MODE_WINDOW;
		break;
	case 'b':
		NWS.BlockPen = argl;
		ParMMode = MODE_WINDOW;
		break;
	case 'c':
		ParMenuColor = argl;
		break;
	case 'f':
		strcpy(ParMConfig.CurCfg, args);
		break;
	case 'r':	/* no drag bar */
		ParMMode = MODE_WINDOW;
		NWS.Flags &= ~WINDOWDRAG;
		break;
	case 'g':	/* depth gadgets */
		ParMMode = MODE_WINDOW;
		NWS.Flags |= WINDOWDEPTH;
		NWS.Width += 51;
		break;
	case 'a':
		ParMMode = MODE_WINDOW;
		NWS.Flags |= NOCAREREFRESH;
		break;
	case 'o':
		if ((fh = Open("NULL:", MODE_NEWFILE)) || (fh = Open("NIL:", MODE_NEWFILE))) {
			ParMProcess->pr_CIS = fh;
			ParMProcess->pr_COS = fh;
			ParMProcess->pr_ConsoleTask = (APTR)((struct FileHandle *)(fh<<2))->fh_Type;
		}
		break;
	case 'h':	/* those can only happen on CLI invocation */
	default:
		Usage();
	}
}


void main(int argc, char *argv[])
{
	struct CommandLineInterface *cli;
	struct DiskObject *dop;
	struct MsgPort *WorkPort;
	struct IntuiMessage *IMsg;
	ULONG Class;
	USHORT Code,Qual;
	BOOL RefreshIt = TRUE;
	char *arg;
	short i;

	TheMenu = &Menu1;
	InitDefaults(&ParMConfig);
	ParMConfig.ReqTitle = "ParM Request";
	ParMConfig.LinkMenu = &Menu1;
	ParMConfig.ItemHeight = 10;			/* 9 in BrowserII */
	ParMConfig.SimpleCmdMode = TRUE;
	FileReq.Dir = Dir;
	FileReq.File = File;
	FileReq.PathName = Path;
	FileReq.dirnamescolor = 3;
	FileReq.devicenamescolor = 3;
	FileReq.stringnamecolor = 2;
	FileReq.stringgadgetcolor = 2;
	FileReq.boxbordercolor = 2;
	FileReq.gadgetboxcolor = 2;
	strcpy(FileReq.Show, "*.cfg");
	ParMProcess = (struct Process *)SysBase->ThisTask;
	strcpy(ParMConfig.CurCfg, DEFAULT_CONFIG_FILE);

	if (WBenchMsg) {	/* Tool Types parsing */
		ParMMode == MODE_WINDOW;	/* may be latter overridden by MYMENU mode */
		if (dop = GetDiskObject(WBenchMsg->sm_ArgList->wa_Name)) {
			ParMConfig.DefaultStack = dop->do_StackSize;
			for( i = 0 ; i < TTANUM ; i++ ) {
				if (arg = FindToolType(dop->do_ToolTypes, TTA_List[i].tta_Name))
					parse_arg(TTA_List[i].tta_cli_equ, arg);
			}
			FreeDiskObject(dop);
		}
		parse_arg('o', "");		/* allways redirect output to NULL: if started from WorkBench */
	}
	else {	/* CLI parsing */
		cli = (struct CommandLineInterface *)((long)ParMProcess->pr_CLI << 2);
		ParMConfig.DefaultStack = cli->cli_DefaultStack << 2;
		for( i = 0 ; i < argc ; i++ ) {
			if (*argv[i] != '-' || strlen(argv[i]) < 2)
				Usage();
			parse_arg(argv[i][1], &argv[i][2]);
		}
	}
	switch(ParMMode) {
	case MODE_WINDOW:
		if (!(Win = OpenWindow(&NWS)))
			Bye(NO_WIN);
		Busy(FALSE);	/* SetWindowTitles() */
		break;
	case MODE_CLI:
		Win = GetWindow();
		if (Win->UserPort) {
			if (Win->IDCMPFlags & MENUPICK) {
				SimpleRequest(ParMConfig.ReqTitle, "This window already have menus.");
				Bye(NO_WIN);
			}
			else {
				PortTaken = TRUE;
				OldIDCMP = Win->IDCMPFlags;
			}
		}
		ModifyIDCMP(Win, Win->IDCMPFlags|MENUPICK|CLOSEWINDOW);
		break;
	case MODE_MYMENU:
		if (Win = FindWorkBench()) {
			if (LastWBMenu->NextMenu) {
				SimpleRequest(ParMConfig.ReqTitle, "Menus already installed in WorkBench.");
				Bye(NO_WIN);
			}
			else
				PortTaken = TRUE;
		}
		else {
			SimpleRequest(ParMConfig.ReqTitle, "Can't find WorkBench.");
			Bye(NO_WIN);
		}
	}
	ParMConfig.Win = Win;
	if (PortTaken)
		TakeMsgPort();
	if (ParMenuColor < 0) ParMenuColor = Win->DetailPen;
	CreateParMenu(ParMenuColor);
	UpDateMenus();

	/* Monitor Menu Events */

	WorkPort = (PortTaken) ? &SwapPort : Win->UserPort;
	while (!quit) {
		WaitPort(WorkPort);
		while (IMsg = (struct IntuiMessage *)GetMsg(WorkPort)) {
			Class = IMsg->Class;
			Code = IMsg->Code;
			Qual = IMsg->Qualifier;
			/* Use raw keys to prevent ALT and CTRL convertions. Allow only Shift qualifiers */
			if (Class == RAWKEY) {
				Code = RawKeyToAscii(Code, Qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT), IMsg->IAddress);
				if (Code != 0 && (Qual & ParMConfig.ShortCutQual) && !(Qual & IEQUALIFIER_REPEAT)) {
					Code = MakeMenuShortCut(TheMenu, Code);
					Class = MENUPICK;
				}
			}
			/*
			 *	PutMsg() conditions (Messages given to port monitored):
			 *	MODE_WINDOW:
			 *		No such port, so all messages replied.
			 *	MODE_MYMENU:
			 *		give all messages but ParM's menus MENUPICK and RAWKEYs converted
			 *		to MENUPICK. handle NEWPREFS after PutMsg().
			 *	MODE_CLI:
			 *		if PortTaken, all but MENUPICK and CLOSEWINDOW.
			 */
			if (!PortTaken
				|| (Class == MENUPICK && (MENUNUM(Code) >= FirstParMMenuNum))
				|| (ParMMode == MODE_CLI && Class == CLOSEWINDOW))
			{
				if (Class == 0x0FL)		/* Msg sent bye 'ParMCD' command */
					ChangeDir((char *)IMsg->IAddress);
				ReplyMsg((struct Message *)IMsg);
			}
			else {
				PutMsg(&SwapPort, (struct Message *)IMsg);
				if (Class == NEWPREFS) {
					while (LastWBMenu->NextMenu)
						Delay(10);
					InstallWBMenus(&Menu1);
				}
				continue;	/* Don't handle these messages */
			}
			switch (Class) {
			case CLOSEWINDOW:
				quit = TRUE;
				break;
			case REFRESHWINDOW:
				if (RefreshIt) {
					if (IsHidden(Win)) {
						WindowToFront(Win);
						RefreshIt = FALSE;	/* prevent refresh caused by WindoToFront() */
					}
				}
				else
					RefreshIt = TRUE;
				break;
			case MENUPICK:
				DoNextSelect = TRUE;
				if (Code != MENUNULL) {
					if (ParMMode == MODE_WINDOW)
						Busy(TRUE);
					do {
						/* Prevent extended selection of workbench menus */
						if (MENUNUM(Code) == FirstParMMenuNum) {
							switch(ITEMNUM(Code)) {
							case OPEN_ITEM:
								OpenMenus();
								DoNextSelect = FALSE;
								break;
							case UPDATE_ITEM:
								UpDateMenus();
								DoNextSelect = FALSE;
								break;
							case STD_CFG_ITEM:
								strcpy(ParMConfig.CurCfg, DEFAULT_CONFIG_FILE);
								UpDateMenus();
								DoNextSelect = FALSE;
								break;
							case CMDMODE_ITEM:
								ParMConfig.SimpleCmdMode = SubItem1.Flags & CHECKED;
								break;
							case COMMAND_ITEM:
								Command(&ParMConfig);
								break;
							case CHDIR_ITEM:
								PathName(ParMProcess->pr_CurrentDir, Dir, REQ_DSIZE);
								FileReq.Flags = FRQDIRONLYM;
								FileReq.Title = "Enter New Dir...";
								if (FileRequester(&FileReq))
									ChangeDir(Dir);
								FileReq.Flags = 0;
								break;
							case QUIT_ITEM:
								quit = TRUE;
								DoNextSelect = FALSE;
							}
						}
						else if (MENUNUM(Code) > FirstParMMenuNum)	/* custom menus */
							DoExtMenu(Code);
					} while (DoNextSelect && (Code = ItemAddress(TheMenu, Code)->NextSelect) != MENUNULL);
					if (ParMMode == MODE_WINDOW)
						Busy(FALSE);
				}
				break;
			}
		}
		/* See if some wb messages have been replied, and free them */
		FreeRepliedWBMessages();
	}
	Bye(THE_END);
}


