/*
 * Prefs programming for setting up Shadow Master. Uses prefs.o as for
 * startup code.
 *
 * Copyright (c) 1991, Mike Meyer
 * All Rights Reserved
 *
 * See the file "ShadowMaster:Distribution"  for information on distribution.
 *
 * ===build instructions
 * % lc guard ; output= guard.o input= guard.c
 * % build prefs.c ; output= prefs.o input= prefs.c
 * % blink prefs.o+guard.o lib lib:amiga.lib+lib:lcr.lib to guard SC SD ; output= guard input= guard.o prefs.o
 * % copy guard //config
 * ===endbuild
 */

#include <string.h>
#include <stdlib.h>

#include <exec/types.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>
#include <utility/tagitem.h>
#include <rexx/rxslib.h>
#include <rexx/storage.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/gadtools.h>
#include <proto/asl.h>
#include <proto/intuition.h>
#include <proto/rexxsys.h>
#include <clib/alib_stdio_protos.h>

/* Values for feed to prefs.o */
int windowheight = 82 ;			/* Height to open the window to */
int windowwidth = 350 ;			/* Width to open the window to */
char *basename = "shadowmaster" ;	/* env:basename/appname.prefs */
char *appname = "guard" ;		/* is the default prefs file */
char *errname = "guard" ;		/* error reports look like errname: text */

/* Values I get from prefs.o, either NULL or open */
extern struct Window *window ;
extern USHORT __far BusyPointerData[] ;
extern void dowbmessage(char *) ;

/* Flags indicating which corners are guarded */
#define TOP_LEFT	1
#define TOP_RIGHT	2
#define BOTTOM_LEFT	4
#define BOTTOM_RIGHT	8

/* The two values I write to the preferences file, plus values for undo */
#define MAXSTRING 200
static int size = 5, old_size = 5, corners = TOP_LEFT, old_corners = TOP_LEFT ;
static char command[MAXSTRING], old_command[MAXSTRING] = "" ;

/* Things I need to save my state */
static struct Gadget *gad_size, *gad_command, *gad_topleft, *gad_topright,
	*gad_bottomleft, *gad_bottomright ;
static int undo_size = FALSE, undo_command = FALSE, undo_corners = FALSE ;
struct RxsLib *RexxSysBase = NULL ;

#define UPDATE_SIZE(value) do { old_size = size; size = value; \
	undo_size = TRUE; if (window) GT_SetGadgetAttrs(gad_size, window, \
	NULL, GTIN_Number, size, TAG_DONE, 0); } while (0)
#define UPDATE_CORNERS(value) do { old_corners = corners; corners = value; \
	undo_corners = TRUE; if (window) { \
	GT_SetGadgetAttrs(gad_topleft, window, NULL, GTCB_Checked, corners & TOP_LEFT, TAG_DONE, 0); \
	GT_SetGadgetAttrs(gad_topright, window, NULL, GTCB_Checked, corners & TOP_RIGHT, TAG_DONE, 0); \
	GT_SetGadgetAttrs(gad_bottomleft, window, NULL, GTCB_Checked, corners & BOTTOM_LEFT, TAG_DONE, 0); \
	GT_SetGadgetAttrs(gad_bottomright, window, NULL, GTCB_Checked, corners & BOTTOM_RIGHT, TAG_DONE, 0); \
	} } while (0)
#define UPDATE_COMMAND(value) do { strcpy(old_command, command); \
	strcpy(command, value); undo_command = TRUE; if (window) \
	GT_SetGadgetAttrs(gad_command, window, NULL, GTST_String, \
	command, TAG_DONE, 0); } while(0)

int LoadFile(char *) ;

/* Functions that do the gadgets. Return TRUE to exit, FALSE otherwise */
static int
select(struct Gadget *g, UWORD code) {
	struct FileRequester	*my_req = NULL ;

	if (!(my_req = AllocAslRequestTags(ASL_FileRequest,
			ASL_TopEdge, 11, ASL_LeftEdge, 0, ASL_Window, window,
			ASL_Dir, "shadowmaster:savers",
			ASL_FrontPen, 1, ASL_BackPen, 0,
			ASL_Hail, "Select Module", TAG_DONE, 0)))
		return FALSE ;

	SetPointer(window, BusyPointerData, 16, 16, -6, 0) ;
	if (RequestFile(my_req)) {
		UPDATE_COMMAND(my_req->rf_File) ;
		undo_size = FALSE ;
		}
	ClearPointer(window) ;
	FreeAslRequest(my_req) ;

	return FALSE ;
	}

static int
config(struct Gadget *g, UWORD code) {
	char config[2 * MAXSTRING], tempfile[MAXSTRING] ;
	BPTR out ;
	char *cp ;

	/* Build strings we need to run */
	sprintf(tempfile, "t:sm.%ld", FindTask(NULL)) ;
	if (!(out = Open(tempfile, MODE_READWRITE))) return FALSE ;
	strcpy(command, ((struct StringInfo *) gad_command->SpecialInfo)->Buffer) ;
	sprintf(config, "shadowmaster:config/%s", command) ;

	/* Run the command (with a busy pointer) */
	SetPointer(window, BusyPointerData, 16, 16, -6, 0) ;
	if (SystemTags(config, SYS_Output, out, TAG_DONE, 0)) {
		sprintf(config, "No config available for\n%s", command) ;
		dowbmessage(config) ;
		}
	else {
		/* Check the output */
		Seek(out, 0, OFFSET_BEGINNING) ;
		if (FGets(out, config, MAXSTRING)) {
			if (cp = strchr(config, '\n')) *cp = '\0' ;
			UPDATE_COMMAND(config) ;
			undo_size = FALSE ;
			}
		}

	/* Now clean up */
	ClearPointer(window) ;
	Close(out) ;
	DeleteFile(tempfile) ;
	return FALSE ;
	}

static char *
RexxIt(char *com, struct MsgPort *port, int flags) {
	struct RexxMsg *msg ;
	struct MsgPort *out ;
	char *result = NULL ;

	if (!(msg = CreateRexxMsg(port, NULL, NULL))) return NULL ;
	msg->rm_Action = RXCOMM | flags ;
	msg->rm_Args[0] = com ;
	if (!FillRexxMsg(msg, 1, 0)) dowbmessage("Internal error: REXX") ;
	else {
		Forbid() ;
		if (out = FindPort("SHADOWMASTER")) PutMsg(out, (struct Message *) msg) ;
		Permit() ;
		if (!out) dowbmessage("ShadowMaster is not running!") ;
		else 
			for (;;) {
				WaitPort(port) ;
				msg = (struct RexxMsg *) GetMsg(port) ;
				if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
					break ;
				msg->rm_Result1 = 10 ;
				msg->rm_Result2 = 10 ;
				ReplyMsg((struct Message *) msg) ;
				}
			
		if (msg->rm_Result1 == 0) result = (char *) msg->rm_Result2 ;
		ClearRexxMsg(msg, 1) ;
		}

	DeleteRexxMsg(msg) ;
	return result ;
	}

static int
test(struct Gadget *g, UWORD code) {
	struct MsgPort *myport ;
	char buffer[2 * MAXSTRING], *old ;

	if (!RexxSysBase
	&& !(RexxSysBase = (struct RxsLib *) OpenLibrary("rexxsyslib.library", 0))) {
		dowbmessage("Can't open rexx.library; test not possible.") ;
		return FALSE ;
		}
	if (!(myport = CreateMsgPort())) {
		dowbmessage("Can't create rexx port; test not possible.") ;
		return FALSE ;
		}

	sprintf(buffer, "command shadowmaster:savers/%s", command) ;
	old = RexxIt(buffer, myport, RXFF_RESULT) ;
	if (old) {			/* It worked */
		RexxIt("blank", myport, 0) ;
		sprintf(buffer, "command %s", old) ;
		DeleteArgstring(old) ;
		RexxIt(buffer, myport, 0) ;
		}
	
	/* Send rexx messages to twiddle the command and blank the thing... */
	DeleteMsgPort(myport) ;
	return FALSE ;
	}

static int
Size(struct Gadget *g, UWORD code) {

	UPDATE_SIZE(((struct StringInfo *) g->SpecialInfo)->LongInt) ;
	undo_command = FALSE ;
	return FALSE ;
	}

static int
Corners(struct Gadget *g, UWORD code){

	UPDATE_CORNERS((g->GadgetID) ^ corners) ;
	return FALSE ;
	}

static int
Command(struct Gadget *g, UWORD code) {

	UPDATE_COMMAND(((struct StringInfo *) g->SpecialInfo)->Buffer) ;
	undo_size = FALSE ;
	return FALSE ;
	}

/* Undos the last action */
int
Undo(void) {
	int save_size, save_corners ;
	char save_command[MAXSTRING] ;

	if (undo_size) {
		save_size = old_size ;
		UPDATE_SIZE(save_size) ;
		}
	if (undo_command) {
		strcpy(save_command, old_command) ;
		UPDATE_COMMAND(save_command) ;
		}
	if (undo_corners) {
		save_corners = old_corners ;
		UPDATE_CORNERS(save_corners) ;
		}
	return FALSE ;
	}

/* Resets the edit data to default values */
int
Defaults(void) {

	UPDATE_COMMAND("black") ;
	UPDATE_SIZE(5) ;
	UPDATE_CORNERS(TOP_LEFT) ;
	return FALSE ;
	}

/* Build the edit gadgets, return TRUE if all, FALSE otherwise */
int
UserGadgets(struct Gadget *gad, struct NewGadget *ng) {

	/* Bottom row: select */
	ng->ng_TopEdge -= ng->ng_Height + 10 ;
	ng->ng_LeftEdge = 13 ;
	ng->ng_GadgetText = "Select" ;
	ng->ng_UserData = &select ;
	if (!(gad = CreateGadgetA(BUTTON_KIND, gad, ng, NULL)))
		return FALSE ;

	/* Bottom row: config */
	ng->ng_LeftEdge = (windowwidth - ng->ng_Width) / 2 ;
	ng->ng_GadgetText = "Config" ;
	ng->ng_UserData = &config ;
	if (!(gad = CreateGadgetA(BUTTON_KIND, gad, ng, NULL)))
		return FALSE ;
	
	/* Bottom row: test */
	ng->ng_LeftEdge = windowwidth - 87 ;
	ng->ng_GadgetText = "Test" ;
	ng->ng_UserData = &test ;
	if (!(gad = CreateGadgetA(BUTTON_KIND, gad, ng, NULL)))
		return FALSE ;

	/* Middle row: Command gadget */
	ng->ng_Flags = NG_HIGHLABEL | PLACETEXT_LEFT ;
	ng->ng_TopEdge -= ng->ng_Height + 3 ;
	ng->ng_LeftEdge = 80 ;
	ng->ng_Width = windowwidth - 100 ;
	ng->ng_GadgetText = "Command" ;
	ng->ng_UserData = &Command ;
	if (!(gad_command = gad = CreateGadget(STRING_KIND, gad, ng,
		GTST_String, command, GTST_MaxChars, MAXSTRING - 1, TAG_DONE, 0)))
		return FALSE ;

	/* Top row: size gadget */
	ng->ng_Flags = NG_HIGHLABEL | PLACETEXT_LEFT ;
	ng->ng_TopEdge -= (ng->ng_Height / 2) + 15 ;
	ng->ng_LeftEdge = 120 ;
	ng->ng_GadgetText = "Size" ;
	ng->ng_UserData = &Size ;
	ng->ng_Width = 67 ;
	if (!(gad_size = gad = CreateGadget(INTEGER_KIND, gad, ng,
		GTIN_Number, size, TAG_DONE, 0)))
		return FALSE ;

	/* Top row: corner gadgets */
	ng->ng_TopEdge -= (ng->ng_Height / 2) ;
	ng->ng_Flags = 0 ;
	ng->ng_Width = 5 ;
	ng->ng_Height = 5 ;
	ng->ng_GadgetText = "" ;
	ng->ng_UserData = &Corners ;
	ng->ng_LeftEdge = 13 ;
	ng->ng_GadgetID = TOP_LEFT ;
	if (!(gad_topleft = gad = CreateGadget(CHECKBOX_KIND, gad, ng,
		GTCB_Checked, corners & TOP_LEFT, TAG_DONE, 0)))
		return FALSE ;

	ng->ng_LeftEdge = 39 ;
	ng->ng_GadgetID = TOP_RIGHT ;
	if (!(gad_topright = gad = CreateGadget(CHECKBOX_KIND, gad, ng,
		GTCB_Checked, corners & TOP_RIGHT, TAG_DONE, 0)))
		return FALSE ;

	ng->ng_TopEdge += 11 ;
	ng->ng_GadgetID = BOTTOM_RIGHT ;
	if (!(gad_bottomright = gad = CreateGadget(CHECKBOX_KIND, gad, ng,
		GTCB_Checked, corners & BOTTOM_RIGHT, TAG_DONE, 0)))
		return FALSE ;

	ng->ng_LeftEdge = 13 ;
	ng->ng_GadgetID = BOTTOM_LEFT ;
	if (!(gad_bottomleft = gad = CreateGadget(CHECKBOX_KIND, gad, ng,
		GTCB_Checked, corners & BOTTOM_LEFT, TAG_DONE, 0)))
		return FALSE ;

	undo_size = undo_command = undo_corners = FALSE ;
	return TRUE;
	}

/* This saves the preferences data to the named file. Returns FALSE if open fails */
int
SaveFile(char *file) {
	BPTR fh ;

	if (!(fh = Open(file, MODE_NEWFILE))) return FALSE ;
	if (window) {
		strcpy(command,
			((struct StringInfo *) gad_command->SpecialInfo)->Buffer) ;
		size = ((struct StringInfo *) gad_size->SpecialInfo)->LongInt ;
		}
	FPrintf(fh, "%ld\n%ld\nshadowmaster:savers/%s\n", corners, size, command) ;
	Close(fh) ;
	return TRUE ;
	}

/* This loads the named file into the editor. Returns FALSE if the open fails */
int
LoadFile(char *file) {
	BPTR fh ;
	int tmp_corners ;
	char *cp, buffer[MAXSTRING] ;

	if (!(fh = Open(file, MODE_OLDFILE))) return FALSE ;
	FGets(fh, buffer, MAXSTRING) ;
	tmp_corners = atoi(buffer) ;
	UPDATE_CORNERS(tmp_corners ? tmp_corners : TOP_LEFT) ;
	FGets(fh, buffer, MAXSTRING) ;
	UPDATE_SIZE(atoi(buffer)) ;
	FGets(fh, buffer, MAXSTRING) ;
	if (cp = strchr(buffer, '\n')) *cp = '\0' ;
	if (cp = strchr(buffer, ':')) cp = strchr(buffer, '/') + 1 ;
	UPDATE_COMMAND(cp ? cp : buffer) ;
	Close(fh) ;

	return TRUE ;
	}

/* This is for me to clean up things with; status is TRUE if we changed env:... */
void
CleanUp(int status) {

	if (RexxSysBase) CloseLibrary((struct Library *) RexxSysBase) ;
	}