/*
 *	BrowserII.c - Copyright © 1991 by S.R. & P.C.
 *
 *	Created:	16 Feb 1991  11:00:11
 *	Modified:	21 Jul 1991  17:19:32
 *
 *	Make>> make
 */

#include "Global.h"
#include "FileList.h"
#include "Process.h"
#include "proto/Windows.h"
#include "proto/Mouse.h"
#include "proto/Config.h"
#include "proto/Menus.h"
#include "proto/Process.h"
#include "proto/Run.h"
#include "proto/Scan.h"
#include "proto/String.h"
#include "proto/Request.h"
#include "proto/Sort.h"
#include "proto/Draw.h"
#include "proto/ActionBack.h"

extern void setmem(void *mem, size_t size, long value);


#define THE_END			0
#define NO_MAINPORT		1
#define NO_PROCESSPORT	2
#define NO_WIN			3
#define NO_DEVS			4


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


extern struct IntuitionBase *IntuitionBase;
extern struct Process *MainProcess;
extern struct MsgPort *MainPort;
extern struct MsgPort *ProcessPort;
extern struct MinList WindowList;
extern struct Screen *Screen;
extern struct BrowserWindow *CurrentWin;
extern struct Config Config;
extern struct ParMConfig ParMConfig;
extern struct Menu Menu1;
extern short NumProcess;
extern char *ReqTitle;
extern BOOL CustomScreen;
extern BPTR BrowserDir;


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

static APTR OldWindowPtr;
static BOOL DoNextSelect;

static void Bye(short);
static void Rescan(void);
static void AffectAll(void);
static void Filters(short);
static void OpenDir(void);
static void UpdateMenus(void);


static void (*FMenu0[])() = {UpdateMenus, CmdMode, DoCommand, ScreenType, RunMode, CopyMode, CopyFlags, DisplayDevs, Options, SaveConfig, Bye};
static void (*FMenu1[])() = {Rescan, OpenParent, OpenDir, SelectMatch, SelectAll, Filters, FileInfo, SortBy, AffectAll};

static void (**MenuFunct[])() = {FMenu0, FMenu1};


/*****						code						*****/


void CheckMenus(void)
{
	struct MenuFlags *MenuFlags;
	BOOL Refresh = FALSE;

	MenuFlags = GetMenuFlags();
	if (MenuFlags->NewDevs) {
		MenuFlags->NewDevs = FALSE;
		ScanDevs();
	}
	if (CurrentWin->bw_Sort != MenuFlags->OldSort && CurrentWin->bw_Type != BW_MAIN) {
		MenuFlags->OldSort = CurrentWin->bw_Sort;
		Sort(CurrentWin);
		Refresh = TRUE;
	}
	if (CurrentWin->bw_EntryInfoFlags != MenuFlags->OldFileInfo && CurrentWin->bw_Type != BW_MAIN) {
		MenuFlags->OldFileInfo = CurrentWin->bw_EntryInfoFlags;
		if (!BuildPrintStrings(CurrentWin)) {
			CloseBrowserWindow(CurrentWin);
			return;
		}
		Refresh = TRUE;
	}
	if (Refresh)
		RefreshWindow(CurrentWin);
}


/* Affect window settings to all windows */

static void AffectAll(void)
{
	struct BrowserWindow *Win;
	BOOL NewFileInfo, NewSort, NewFilters;

	Win = (struct BrowserWindow *)WindowList.mlh_Head;
	while(Win->bw_Node.mln_Succ) {
		NewFileInfo = NewSort = NewFilters = FALSE;
		/* FileInfos */
		if (Win->bw_EntryInfoFlags != CurrentWin->bw_EntryInfoFlags) {
			Win->bw_EntryInfoFlags = CurrentWin->bw_EntryInfoFlags;
			NewFileInfo = TRUE;
		}
		/* Sort */
		if (Win->bw_Sort != CurrentWin->bw_Sort) {
			Win->bw_Sort = CurrentWin->bw_Sort;
			NewSort = TRUE;
		}
		/* Filters */
		if (Win->bw_FiltersInfo != CurrentWin->bw_FiltersInfo) {
			Win->bw_FiltersInfo = CurrentWin->bw_FiltersInfo;
			/* List2Array() also does Sort() and BuildPrintStrings() */
			NewFileInfo = FALSE;
			NewSort = FALSE;
			NewFilters = TRUE;
		}
		if (Win->bw_Type != BW_MAIN && (NewFileInfo | NewSort | NewFilters)) {
			if (NewFilters) {
				if (List2Array(Win))
					MakeBottomInfoString(Win);
				else {
					CloseBrowserWindow(Win);
					return;
				}
			}
			if (NewFileInfo && !BuildPrintStrings(Win)) {
				CloseBrowserWindow(Win);
				return;
			}
			if (NewSort)
				Sort(Win);
			RefreshWindow(Win);
		}
		Win = (struct BrowserWindow *)Win->bw_Node.mln_Succ;
	}
}


static void Filters(short sub)
{
	struct SelectInfo *FI;
	BOOL Refresh = FALSE;

	SetWaitPointer(TRUE);
	FI = &CurrentWin->bw_FiltersInfo;
	switch(sub) {
	case 0:		/* Set... */
		if (FiltersReq(FI ,FILTERS_REQ))
			Refresh = TRUE;
		break;
	case 1:		/* Prefs */
		CopyMem(&Config.DefaultFilters, FI, sizeof(struct SelectInfo));
		Refresh = TRUE;
		break;
	case 2:		/* No .info */
		if (FI->si_Flags & SI_INVERT) {
			FI->si_Flags &= ~SI_INVERT;
			Refresh = TRUE;
		}
		if (Strcmp(FI->si_Pattern, "~*.info")) {
			strcpy(FI->si_Pattern, "~*.info");
			PreParse("~*.INFO", FI->si_PatTok);
			FI->si_Flags = (FI->si_Flags | (SI_NAME|SI_MATCH_FILES)) & ~SI_ALL_FILES;
			Refresh = TRUE;
		}
		break;
	case 3:		/* Clear */
		if (FI->si_Flags != (SI_ALL_FILES|SI_ALL_DIRS)) {
			setmem(FI, sizeof(struct SelectInfo), 0);
			FI->si_Flags = SI_ALL_FILES|SI_ALL_DIRS;
			Refresh = TRUE;
		}
		break;
	case 4:		/* Invert */
		FI->si_Flags ^= SI_INVERT;
		Refresh = TRUE;
		break;
	}
	if (Refresh && CurrentWin->bw_Type == BW_DIR) {
		if (List2Array(CurrentWin)) {
			MakeBottomInfoString(CurrentWin);
			RefreshWindow(CurrentWin);
		}
		else
			CloseBrowserWindow(CurrentWin);
	}
	SetWaitPointer(FALSE);
}


/* Atcho, bonsoir...  */

void exit(int);

static void Bye(short err)
{
	if (NumProcess > 0) {
		SimpleRequest(ReqTitle, "Processes still running.");
		return;
	}
	switch(err) {
	case 0x001F:	/* SUBNUM() when no submenu */
		err = 0;
	case THE_END:
	case NO_DEVS:
		MainProcess->pr_WindowPtr = OldWindowPtr;
		while(WindowList.mlh_Head->mln_Succ)
			CloseBrowserWindow((struct BrowserWindow *)WindowList.mlh_TailPred);
		if (CustomScreen)
			CloseScreen(Screen);
		FreeMenus(&ParMConfig);
	case NO_WIN:
		DeletePort(ProcessPort);
	case NO_PROCESSPORT:
		DeletePort(MainPort);
	case NO_MAINPORT:
		;
	}

#ifdef TRACKER
	freeall();
#endif

	exit(err);
}


void Rescan(void)
{
	if (CurrentWin->bw_Type == BW_MAIN) {
		if (!ScanDevs())
			Bye(NO_DEVS);
	}
	else if (!ScanDir(CurrentWin))
		CloseBrowserWindow(CurrentWin);
}


void main(int argc, char *argv[])
{
	struct IntuiMessage *IMsg;
	struct BrowserMsg *BMsg;
	struct TaskData *TaskData;
	ULONG WaitMask, Class;
	USHORT Code,Qual;
	SHORT X,Y;
	ULONG Sec, Mic;
	struct BrowserWindow *Win;
	BPTR RootLock, fh;
	void (*DoMenu)(short);

#ifdef TRACKER
	if (!inittrack())
		exit(0);
	fh = NULL;	/* prevent 'variable not used' compile warning */
#endif

	/* give a TaskData even to main process for actions in syncronous mode */
	TaskData = ArpAlloc(sizeof(struct TaskData));
	TaskData->td_InitialDir = BrowserDir;
	MainProcess->pr_Task.tc_UserData = (APTR)TaskData;
	if (!(MainPort = CreatePort(0,0)))
		Bye(NO_MAINPORT);
	if (!(ProcessPort = CreatePort(0,0)))
		Bye(NO_PROCESSPORT);
	WaitMask = (1L << MainPort->mp_SigBit) | (1L << ProcessPort->mp_SigBit);
	NewMinList(&WindowList);	
	InitDefaults(&ParMConfig);
	ParMConfig.ReqTitle = ReqTitle;
	ParMConfig.ItemHeight = 9;
	strcpy(ParMConfig.CurCfg, BROWSERII_MENU_FILE);
	LoadConfig();	/* Load config file ! */
	InitMenus();	/* Init checkmarks for global "BrowserII" menu */
	if (!OpenBrowserWindow(NULL, NULL, NULL, 0))		/* Open main window */
		Bye(NO_WIN);
	/* Make requesters appear in browser screen */
	OldWindowPtr = MainProcess->pr_WindowPtr;

#ifndef TRACKER
	MainProcess->pr_WindowPtr = (APTR)-1;	/* Prevent request if NULL: is not mounted */
	if ((fh = Open("NULL:", MODE_NEWFILE)) || (fh = Open("NIL:", MODE_NEWFILE))) {
		MainProcess->pr_CIS = fh;
		MainProcess->pr_COS = fh;
		MainProcess->pr_ConsoleTask = (APTR)((struct FileHandle *)(fh<<2))->fh_Type;
	}
#endif

	MainProcess->pr_WindowPtr = (APTR)CurrentWin->bw_Window;
	ParMConfig.Win = CurrentWin->bw_Window;	/* Just for BlockPen check in menu alloc */
	if (!ScanDevs())
		Bye(NO_DEVS);
	UpdateMenus();
	SetTaskPri((struct Task *)MainProcess, 1);

	/* Monitor Menu Events */

	for(;;) {
		Wait(WaitMask);
		while (BMsg = (struct BrowserMsg *)GetMsg(ProcessPort)) {
			switch(BMsg->bm_Type) {
			case BM_STARTUP:	/* Reply. End of process */
				NumProcess--;
				FreeBrowserMsg(BMsg);
				break;
			case BM_ACTIONBACK:
				DoActionBack(&BMsg->bm_Info.ActionBack.sfi, BMsg->bm_Info.ActionBack.SrcDir, BMsg->bm_Info.ActionBack.DestDir);
				ReplyMsg((struct Message *)BMsg);
				break;
			case BM_UPDATEDIR:
				DoUpdateDir(BMsg->bm_Info.Dir);
				ReplyMsg((struct Message *)BMsg);
				break;
			}
		}
		FreeRepliedWBMessages();
		while (IMsg = (struct IntuiMessage *)GetMsg(MainPort)) {
			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(&Menu1, Code);
					Class = MENUPICK;
				}
			}
			if (Class != DISKINSERTED && Class != DISKREMOVED && IMsg->IDCMPWindow != CurrentWin->bw_Window) {
				CurrentWin = FindWindow(IMsg->IDCMPWindow);
				SetMenus(CurrentWin);
				MakeMainBottomInfoString();
			}
			X = IMsg->MouseX;
			Y = IMsg->MouseY;
			Sec = IMsg->Seconds;
			Mic = IMsg->Micros;
			ReplyMsg((struct Message *)IMsg);
			switch(Class) {
			case MOUSEBUTTONS:
				switch(Code) {
				case SELECTDOWN:
					DoSelectDown(X, Y, Sec, Mic, Qual);
					break;
				case SELECTUP:
					DoSelectUp(X, Y);
					break;
				}
				break;
			case NEWSIZE:
				RefreshWindow(CurrentWin);
				break;
			case GADGETDOWN:
				RealTimeScroll(&CurrentWin->bw_ScrollStruct);
				break;
			case GADGETUP:
				break;		/* Nothing yet ! */
			case MENUPICK:
				DoNextSelect = TRUE;
				if (Code != MENUNULL) {
					do {
						switch(MENUNUM(Code)) {
						case 0:
						case 1:
							DoMenu = (MenuFunct[MENUNUM(Code)])[ITEMNUM(Code)];
							if (DoMenu) DoMenu(SUBNUM(Code));
							break;
						case 2:
							RunAction(NULL, ITEMNUM(Code));
							break;
						default:
							DoNextSelect = DoExtMenu(Code);
							if (!(Config.Options & OPT_KEEPSELECTED))
								DeselectAll(NULL);
						}
					} while (DoNextSelect && (Code = ItemAddress(&Menu1, Code)->NextSelect) != MENUNULL);
					CheckMenus();
				}
				break;
			case DISKINSERTED:
			case DISKREMOVED:
				if (!ScanDevs())
					Bye(NO_DEVS);
				break;
			case CLOSEWINDOW:
				RootLock = DupLock(CurrentWin->bw_RootLock);
				CloseBrowserWindow(CurrentWin);
				if (Qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) {
					Win = (struct BrowserWindow *)WindowList.mlh_Head->mln_Succ;
					while(Win->bw_Node.mln_Succ) {
						if (!CompareLock(Win->bw_RootLock, RootLock)) {
							CloseBrowserWindow(Win);
							Win = (struct BrowserWindow *)WindowList.mlh_Head->mln_Succ;
						}
						else
							Win = (struct BrowserWindow *)Win->bw_Node.mln_Succ;
					}
				}
				UnLock(RootLock);
				break;
			}
		}
	}
}


