/*
 * startup code for preferences programs.
 *
 * Copyright (c) 1991, Mike Meyer
 * All Rights Reserved
 *
 * See the file "ShadowMaster:Distribution"  for information on distribution.
 *
 * ===build instructions
 * % lc prefs ; output= prefs.o input= prefs.c
 * ===endbuild
 */

#include <dos.h>
#include <string.h>

#include <exec/types.h>
#include <exec/execbase.h>
#include <utility/tagitem.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/asl.h>
#include <proto/icon.h>
#include <clib/alib_stdio_protos.h>

/* User supplied values */
extern int windowheight ;		/* Height to open the window to */
extern int windowwidth ;		/* Width to open the window to */
extern char *basename ;			/* env:basename/appname.prefs */
extern char *appname ;			/* is the default prefs file */
extern char *errname ;			/* error reports look like errname: text */

/* User functions */
extern int SaveFile(char *) ;		/* Save current preferences to named file */
extern int LoadFile(char *) ;		/* Load preferences from named file */
extern int Undo(void) ;			/* Undo last action */
extern int Defaults(void) ;		/* Reset to defaults */
extern int UserGadgets(struct Gadget *,	/* Add user gadgets to window */
		struct NewGadget *) ;
extern void CleanUp(int) ;		/* Cleans up before exit */

/* My functions, supplied here */
static int OpenFile(void) ;
static int SaveAs(void) ;
static int Quit(void) ;
static int gQuit(struct Gadget *, UWORD) ;
static int LastSaved(void) ;
static int Icons(void) ;
static int Use(struct Gadget *, UWORD) ;
static int Save(struct Gadget *, UWORD) ;
static int Restore(void) ;
static void SaveIcon(char *) ;
void dowbmessage(char *) ;

/* Static data for the world */
static int icons = TRUE ;		/* Create icons on save? */
static char save[FMSIZE] ;		/* envarc: file name */
static char use[FMSIZE] ;		/* env: file name */
static char *edit ;			/* File we were invoked on */
struct Window *window = NULL ;		/* So I can set the wait pointer for it */

/* Library bases we allow the user to access */
struct ExecBase		*SysBase = NULL ;
struct DosLibrary	*DOSBase = NULL ;
struct IntuitionBase	*IntuitionBase = NULL ;
struct GfxBase		*GfxBase = NULL ;
struct Library		*GadToolsBase = NULL ;
struct Library		*AslBase = NULL ;
struct Library		*IconBase = NULL ;

/* Initialization data */
static struct TextAttr		topaz_8 = { "topaz.font", 8, 0, 0 } ;/* Blame this on GadTools */

static struct NewMenu pref_menu[] = {
	{ NM_TITLE, "Project",		0,   0, 0, 0},
	{ NM_ITEM,  "Open...",		"O", 0, 0, (APTR) &OpenFile},
	{ NM_ITEM,  "Save As...",	"A", 0, 0, (APTR) &SaveAs},
	{ NM_ITEM,  NM_BARLABEL,	0,   0, 0, 0},
	{ NM_ITEM,  "Quit",		"Q", 0, 0, (APTR) &Quit},

	{ NM_TITLE, "Edit",		0,   0, 0, 0},
	{ NM_ITEM,  "Reset to Default", "D", 0, 0, (APTR) &Defaults},
	{ NM_ITEM,  "Last Saved",	"L", 0, 0, (APTR) &LastSaved},
	{ NM_ITEM,  "Restore",		"R", 0, 0, (APTR) &Restore},
	{ NM_ITEM,  "Undo",		"U", 0, 0, (APTR) &Undo},

	{ NM_TITLE, "Options",		0,   0, 0, 0},
	{ NM_ITEM,  "Create Icons?",	0,   CHECKIT|MENUTOGGLE|CHECKED, 0,
								(APTR) &Icons},
	{ NM_END,   0,			0,   0, 0, 0}
	} ;

#define TEMPLATE "FROM,EDIT/S,USE/S,SAVE/S,PUBSCREEN/K"
static enum {FROM_POS, EDIT_POS, USE_POS, SAVE_POS, SCREEN_POS} ;

#define done(x, m) do {mess = m; status = x; goto out; } while (0)
static int __saveds
start(void) {
	int			status = RETURN_OK, (*menufunc)(void),
				(*gadfunc)(struct Gadget *, UWORD), i,
				action_use = FALSE, action_save = FALSE ;
	char			*mess = NULL, *tool ;
	long			opts[5] = {&use[0], 1, 0, 0, 0} ;
	WORD			iconic[] = {0, 0, 200, 11} ;
	BPTR			olddir = NULL ;
	struct Menu		*menu = NULL ;
	struct MenuItem		*menuitem = NULL ;
	USHORT			menunumber ;
	struct Screen		*pubsc = NULL ;
	struct RDArgs		*args = NULL ;
	struct TextFont		*font = NULL ;
	struct IntuiMessage	*bounce, in ;
	struct Gadget		*gadget_crap = NULL, *gadtools_turd, *ingad ;
	struct NewGadget	no_good ;
	struct WBStartup	*wbmessage = NULL ;
	struct WBArg		*wbargs ;
	struct DiskObject	*my_icon ;

	/* Open the libraries */
	SysBase = *((struct ExecBase **) 4);
	if (!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
		done(RETURN_FAIL, "You need 2.04 or later") ;
	if (!(IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", 37)))
	    	done(RETURN_FAIL, "Can't open intuition.library") ;
	if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 37)))
		done(RETURN_FAIL, "Can't open graphics.library") ;
	if (!(GadToolsBase = OpenLibrary("gadtools.library", 37)))
		done(RETURN_FAIL, "Can't open gadtools.library") ;
	if (!(AslBase = OpenLibrary("asl.library", 37)))
		done(RETURN_FAIL, "Can't open asl.library") ;
	if (!(IconBase = OpenLibrary("icon.library", 37)))
		done(RETURN_FAIL, "Can't open icon.library") ;

	/* Make file names that we edit */
	sprintf(save, "envarc:%s/%s.prefs", basename, appname) ;
	sprintf(use, "env:%s/%s.prefs", basename, appname) ;

	/* Argument processing, if needed */
	if (((struct Process *) SysBase->ThisTask)->pr_CLI) {	/* do CLI args */
		if (!(args = ReadArgs(TEMPLATE, opts, NULL)))
			done(RETURN_ERROR, "Invalid arguments") ;
		edit = (char *) opts[FROM_POS] ;
		action_use = opts[USE_POS] ;
		action_save = opts[SAVE_POS] ;
		}
	else {	/* Do WB args */
		edit = use ;		/* To make sure we have *something* */
		WaitPort(&((struct Process *) SysBase->ThisTask)->pr_MsgPort) ;
		wbmessage = (struct WBStartup *)
			GetMsg(&((struct Process *) SysBase->ThisTask)->pr_MsgPort) ;
		wbargs = wbmessage->sm_ArgList ;
		i = wbmessage->sm_NumArgs ;
		olddir = CurrentDir(wbargs->wa_Lock) ;
		while (i-- > 0) {
			CurrentDir(wbargs->wa_Lock) ;
			if (my_icon = GetDiskObjectNew(wbargs->wa_Name)) {
				if (my_icon->do_Type == WBPROJECT)
					edit = wbargs->wa_Name ;
				if (tool = FindToolType(my_icon->do_ToolTypes, "ACTION"))
					if (!stricmp(tool, "use")) {
						action_use = TRUE ;
						i = 0 ;
						}
					else if (!stricmp(tool, "save")) {
						action_save = TRUE ;
						i = 0 ;
						}
					else if (!stricmp(tool, "edit")) i = 0 ;
				FreeDiskObject(my_icon) ;
				}
			wbargs += 1 ;
			}
		}
		
	/* Load up file we want to work on, and run it */
	if (!LoadFile(edit)) Defaults() ;
	if (action_save) {
		Save(NULL, 0) ;
		done(RETURN_OK, NULL) ;
		}
	if (action_use) {
		Use(NULL, 0) ;
		done(RETURN_OK, NULL) ;
		}

	/* Otherwise, we're editing, so open the bloody window... */

	if (!(pubsc = LockPubScreen((char *) opts[SCREEN_POS])))
		done(RETURN_ERROR, "Can't lock requested public screen") ;
	if (!(no_good.ng_VisualInfo = GetVisualInfoA(pubsc, NULL)))
		done(RETURN_FAIL, "internal error: VISINFO") ;
	if (!(font = OpenFont(&topaz_8)))
		done(RETURN_FAIL, "Can't open topaz font") ;
	if (!(menu = CreateMenusA(pref_menu, NULL)))
		done(RETURN_FAIL, "Internal error: MENUS") ;
	if (!LayoutMenusA(menu, no_good.ng_VisualInfo, NULL))
		done(RETURN_FAIL, "Internal error: LAYOUT") ;

	/* Set up the unchanging no good gadget stuff */
	if (!(gadtools_turd = CreateContext(&gadget_crap)))
		done(RETURN_FAIL, "Internal error: CONTEXT") ;
	no_good.ng_TextAttr = &topaz_8 ;
	no_good.ng_Flags = no_good.ng_GadgetID = 0 ;
	no_good.ng_Height = 12 ;
	no_good.ng_Width = 67 ;
	no_good.ng_TopEdge = pubsc->WBorTop + pubsc->Font->ta_YSize + windowheight - 13 ;

	/* standard cancel gadget */
	no_good.ng_LeftEdge = windowwidth - 87 ;
	no_good.ng_GadgetText = "Cancel" ;
	no_good.ng_UserData = (APTR) &gQuit ;
	if (!(gadtools_turd = CreateGadgetA(BUTTON_KIND, gadtools_turd, &no_good, NULL)))
		done(RETURN_FAIL, "internal error: CANCEL") ;

	/* standard use gadget */
	no_good.ng_LeftEdge = (windowwidth - no_good.ng_Width) / 2 ;
	no_good.ng_GadgetText = "Use" ;
	no_good.ng_UserData = (APTR) &Use ;
	if (!(gadtools_turd = CreateGadgetA(BUTTON_KIND, gadtools_turd, &no_good, NULL)))
		done(RETURN_FAIL, "internal error: USE") ;

	/* standard save gadget */
	no_good.ng_LeftEdge = 13 ;
	no_good.ng_GadgetText = "Save" ;
	no_good.ng_UserData = (APTR) &Save ;
	if (!(gadtools_turd = CreateGadgetA(BUTTON_KIND, gadtools_turd, &no_good, NULL)))
		done(RETURN_FAIL, "internal error: SAVE") ;

	/* The users gadgets (if any) */
	if (!UserGadgets(gadtools_turd, &no_good))
		done(RETURN_FAIL, "internal error: GADGETS") ;

	if (!(window = OpenWindowTags(NULL,
			WA_Left, 0, WA_InnerWidth, windowwidth,
			WA_Top, 11, WA_InnerHeight, windowheight, WA_AutoAdjust, 1,
			WA_Title, "ScreenSaver Preferences",
			WA_DragBar, 1, WA_Zoom, iconic, WA_DepthGadget, 1,
			WA_Gadgets, gadget_crap,
			WA_IDCMP, LISTVIEWIDCMP|IDCMP_MENUPICK,
			WA_PubScreen, pubsc, WA_PubScreenFallBack, 1,
			TAG_DONE, 0)))
		done(RETURN_FAIL, "Can't open window") ;

	SetMenuStrip(window, menu) ;
	GT_RefreshWindow(window, NULL) ;

	FOREVER {
		WaitPort(window->UserPort) ;
		while (bounce = GT_GetIMsg(window->UserPort)) {
			in = *bounce ;
			GT_ReplyIMsg(bounce) ;
			switch (in.Class) {
				case IDCMP_GADGETUP:
					ingad = (struct Gadget *) in.IAddress ;
					gadfunc = (int (*)()) ingad->UserData ;
					if (gadfunc(ingad, in.Code))
						done(RETURN_OK, NULL) ;
					break ;
				case IDCMP_MENUPICK:
					menunumber = in.Code ;
					while (menunumber != MENUNULL) {
						menuitem = ItemAddress(menu, menunumber) ;
						menufunc = (int (*)()) GTMENUITEM_USERDATA(menuitem) ;
						if (menufunc()) done(RETURN_OK, NULL) ;
						menunumber = menuitem->NextSelect ;
						}
					break ;

				case IDCMP_REFRESHWINDOW:
					GT_BeginRefresh(window);
					GT_EndRefresh(window, TRUE);
					break ;
				}
			}
		}

out:
	if (mess && DOSBase) {
		if (wbmessage && IntuitionBase) dowbmessage(mess) ;
		else {
			PutStr(errname) ;
			PutStr(": ") ;
			PutStr(mess) ;
			PutStr("\n") ;
			}
		}
			
	if (window) {
		ClearMenuStrip(window) ;
		CloseWindow(window) ;
		}
	if (gadget_crap) FreeGadgets(gadget_crap) ;
	if (pubsc) UnlockPubScreen(NULL, pubsc) ;
	if (no_good.ng_VisualInfo) FreeVisualInfo(no_good.ng_VisualInfo) ;
	if (font) CloseFont(font) ;
	if (menu) FreeMenus(menu) ;
	if (args) FreeArgs(args) ;
	if (olddir) CurrentDir(olddir) ;
	if (IconBase) CloseLibrary(IconBase) ;
	if (AslBase) CloseLibrary(AslBase) ;
	if (GfxBase) CloseLibrary((struct Library *) GfxBase) ;
	if (GadToolsBase) CloseLibrary(GadToolsBase) ;
	if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase) ;
	if (wbmessage) {
		Forbid() ;
		ReplyMsg((struct Message *) wbmessage) ;
		}
	if (DOSBase) CloseLibrary((struct Library *) DOSBase) ;
	return status ;
	}

/* The functions that actually *do* things */
/* Note: an exit that *doesn't* change env:... must go through here */
static int
Quit(void) {
	CleanUp(FALSE) ;
	return 1 ;
	}
static int gQuit(struct Gadget *g, UWORD code) { return Quit(); }

static int
LastSaved(void) {
	if (!LoadFile(save)) dowbmessage("Could not load saved file") ;
	return 0 ;
	}

static int
Icons(void) {
	icons = !icons ;	/* Tacky, but so is the menu facility */
	return 0 ;
	}

/* Note: an exit that changes env:... must go through herre! */
static int
Use(struct Gadget *g, UWORD code) {
	if (!SaveFile(use)) {
		dowbmessage("Could not save file in env:") ;
		return 0 ;
		}
	CleanUp(TRUE) ;
	return 1 ;
	}

static int
Save(struct Gadget *g, UWORD code) {
	if (!SaveFile(save)) {
		dowbmessage("Could not save file in envarc:") ;
		return 0 ;
		}
	return Use(g, code) ;
	}

static int
Restore(void) {
	if (!LoadFile(edit)) dowbmessage("Could not reload file") ;
	return 0 ;
	}

/* 2.0 Busy Pointer data From of Steve Tibbett's PointerX */
USHORT __chip BusyPointerData[] = {
        0x0000,0x0000,
        0x0400,0x07C0,0x0000,0x07C0,0x0100,0x0380,0x0000,0x07E0,
        0x07C0,0x1FF8,0x1FF0,0x3FEC,0x3FF8,0x7FDE,0x3FF8,0x7FBE,
        0x7FFC,0xFF7F,0x7EFC,0xFFFF,0x7FFC,0xFFFF,0x3FF8,0x7FFE,
        0x3FF8,0x7FFE,0x1FF0,0x3FFC,0x07C0,0x1FF8,0x0000,0x07E0,
        0x0000,0x0000,
        } ;

static char *
GetFileName(char *prompt, int flags) {
	struct FileRequester	*my_req = NULL ;
	char filename[FNSIZE], hail[FMSIZE], *out = NULL ;
	static char dirname[FMSIZE] ;

	sprintf(filename, "%s.prefs", appname) ;
	sprintf(dirname, "envarc:%s", basename) ;
	sprintf(hail, "%s %s preferences", prompt, appname) ;

	if (!(my_req = AllocAslRequestTags(ASL_FileRequest,
			ASL_TopEdge, 11, ASL_LeftEdge, 0, ASL_Window, window,
			ASL_Dir, dirname, ASL_File, filename, ASL_FuncFlags, flags,
			ASL_Hail, hail, TAG_DONE, 0)))
		return NULL ;

	SetPointer(window, BusyPointerData, 16, 16, -6, 0) ;
	if (RequestFile(my_req)) {
		strcpy(dirname, my_req->rf_Dir) ;
		AddPart(dirname, my_req->rf_File, FMSIZE) ;
		out = dirname ;
		}
	ClearPointer(window) ;
	FreeAslRequest(my_req) ;
			
	return out ;
	}

static int
SaveAs(void) {
	struct DiskObject *deficon ;
	char defname[FMSIZE] ;
	char *file = GetFileName("Save", FILF_SAVE) ;

	if (!file) return 0 ;
	if (!SaveFile(file)) dowbmessage("Could not open file") ;
	else if (icons) {
		sprintf(defname, "env:%s/%s", basename, appname) ;
		/* If there is no default icon, we don't create them */
		if (deficon = GetDiskObjectNew(defname)) {
			PutDiskObject(file, deficon) ;
			FreeDiskObject(deficon) ;
			}
		}
	return 0 ;
	}

static int
OpenFile(void) {
	char *file = GetFileName("Load", 0) ;
	if (!file) return 0 ;
	if (!LoadFile(file)) dowbmessage("Could not open file") ;
	return 0 ;
	}

void
dowbmessage(char *message) {
	struct EasyStruct req ;

	req.es_StructSize = sizeof(req) ;
	req.es_Flags = 0 ;
	req.es_Title = errname ;
	req.es_TextFormat = message ;
	req.es_GadgetFormat = "Okay" ;
	EasyRequestArgs(window, &req, NULL, NULL) ;
	}
