/*-
 * PREFSMAIN.C
 *
 * The main file of the xpkdisk preferences utility.
 *
 * $Id: prefsmain.c,v 1.5 1995/04/08 20:23:48 Rhialto Exp $
 * $Log: prefsmain.c,v $
 * Revision 1.5  1995/04/08  20:23:48  Rhialto
 * Add/correct version strings.
 *
 * Revision 1.4  1995/04/02  14:58:51  Rhialto
 * Change #ifdef into #if.
 * Update for DICE 3.0. Lots more casts.
 *
 * Revision 1.3  1993/11/08  13:11:15  Rhialto
 * Add RCS tags.
 *
 *
 * This code is (C) Copyright 1992-1995 by Olaf Seibert. All rights reserved.
 * May not be used or copied without a licence.
-*/

#define INTUI_V36_NAMES_ONLY

#include "xpkdisk.h"

#ifndef LIBRARIES_GADTOOLS_H
#include <libraries/gadtools.h>
#endif
#ifndef LIBRARIES_ASL_H
#include <libraries/asl.h>
#endif
#ifndef INTUITION_GADGETCLASS_H
#include <intuition/gadgetclass.h>
#endif
#ifndef DOS_DOS_H
#include <dos/dos.h>
#endif
#ifndef DOS_DOSEXTENS_H
#include <dos/dosextens.h>
#endif
#ifndef DOS_FILEHANDLER_H
#include <dos/filehandler.h>
#endif
#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif

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

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/asl_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

#include "prefswindow.h"

static const char rcsId[] = "$Id: prefsmain.c,v 1.5 1995/04/08 20:23:48 Rhialto Exp $";

struct Library *GadToolsBase;
struct IntuitionBase *IntuitionBase;
struct Library *AslBase;
extern struct ExecBase *SysBase;

struct Settings {
    short	    MaxCache;
    ushort	    CacheFlags;
    char	    CacheCMDUPDATE;
    char	    CacheDELAY;
    char	    CacheSAFE;
    char	    Licensed;
    short	    CacheTimeout;
    char	    XPKPackMethod[10];	 /* "XXXX.100" */
};

const char	DevName[] = "xpkdisk.device";
long		DevUnit;
struct XpkDiskUnit *Unit;
struct MsgPort *ReplyPort;
struct IOExtTD *IOReq;
struct FileRequest *FileRequest;
struct Settings Defaults = {
    MAX_CACHE,
    CACHE_FLAGS,
    (CACHE_FLAGS & CACHEF_CMDUPDATE) != 0,
    (CACHE_FLAGS & CACHEF_DELAY) != 0,
    (CACHE_FLAGS & CACHEF_SAFEWRITE) != 0,
    1,
    CACHE_TIMEOUT,
    PACKING_METHOD
};
struct Settings Settings;
struct Settings Initial;

const char	idString[] = "\0$VER: xdPrefs " STR(VERSION) "." STR(REVISION) "\r\n";
const char	OkString[] = "Ok";
const char	AbortString[] = "Abort";
const char	PanicString[] = "Panic!";
const char	Env[] = CONFIGFILE;
const char	EnvArc[] = CONFIGFILE_ARC;

void		Show(void);
void		Hide(void);
void		SetGadgetAttr(int id, ULONG tag, ULONG value);
int		Save(char *filename, int notify);
void		SelectUnit(int unit);
void		DeselectUnit(void);
extern int	ReadConfig(UNIT *unit, char *buf, int sz);

#define DONE_DONE   1

void
GT_SetGadgetAttrs(struct Gadget *gad, struct Window *win,
		  struct Requester *req, Tag tag1, ... )
{
    GT_SetGadgetAttrsA(gad, win, req, (void *)&tag1);
}

APTR
GetVisualInfo(struct Screen *screen, Tag tag1, ... )
{
    return GetVisualInfoA(screen, (void *)&tag1);
}

void
DrawBevelBox(struct RastPort *rport, long left, long top, long width,
	     long height, Tag tag1, ... )
{
    DrawBevelBoxA(rport, left, top, width, height, (void *)&tag1);
}

struct Menu *
CreateMenus(struct NewMenu *newmenu, Tag tag1, ... )
{
    return CreateMenusA(newmenu, (void *)&tag1);
}

BOOL
LayoutMenus(struct Menu *firstmenu, APTR vi, Tag tag1, ... )
{
    return LayoutMenusA(firstmenu, vi, (void *)&tag1);
}

long min(long a, long b)
{
    return a < b? a : b;
}

void
Die(void)
{
    Hide();
    DeselectUnit();

    if (IOReq) {
	DeleteExtIO((struct IORequest *)IOReq);
	IOReq = NULL;
    }

    if (ReplyPort) {
	DeletePort(ReplyPort);
	ReplyPort = NULL;
    }

    if (AslBase) {
	if (FileRequest) {
	    FreeAslRequest(FileRequest);
	    FileRequest = NULL;
	}

	CloseLibrary(AslBase);
	AslBase = NULL;
    }
    if (IntuitionBase) {
	CloseLibrary(IntuitionBase);
	IntuitionBase = NULL;
    }
    if (GadToolsBase) {
	CloseLibrary(GadToolsBase);
	GadToolsBase = NULL;
    }
    exit(0);
}

void
OpenAll(int argc, char **argv)
{
    extern struct DosLibrary *DOSBase;

    if (DOSBase->dl_lib.lib_Version < 37) {
	puts("Sorry, requires at least V37...");
	Die();
    }
    GadToolsBase = OpenLibrary("gadtools.library", 37L);
    if (!GadToolsBase)
	Die();

    IntuitionBase = OpenLibrary("intuition.library", 37L);
    if (!IntuitionBase)
	Die();

    AslBase = OpenLibrary("asl.library", 37L);
    if (!AslBase)
	Die();

    ReplyPort = CreatePort(NULL, 0);
    if (!ReplyPort)
	Die();

    IOReq = (struct IOExtTD *)CreateExtIO(ReplyPort, sizeof(*IOReq));
    if (!IOReq)
	Die();

    Show();
}

void
Show(void)
{
    if (PrefsWnd) {
	WindowToFront(PrefsWnd);
	return; 		/* already open */
    }

    if (SetupScreen())
	Die();

    if (OpenPrefsWindow())
	Die();

    SelectUnit(DevUnit);
}

void
Hide(void)
{
    if (PrefsWnd) {
	struct IntuiMessage *msg;

	while (msg = GT_GetIMsg(PrefsWnd->UserPort)) {
	    GT_ReplyIMsg(msg);
	}
    }

    ClosePrefsWindow();
    CloseDownScreen();
}

void
SetGadgetAttr(int id, ULONG tag, ULONG value)
{
    GT_SetGadgetAttrs(PrefsGadgets[id], PrefsWnd, NULL,
		      tag, value,
		      TAG_END);
}

int
Confirm(char *fmt, char *buttons, ...)
{
    static struct EasyStruct er0 = {
	sizeof er0, 0, "xpkdisk Prefs",
	NULL,
	NULL
    };
    struct EasyStruct er;
    va_list	    va;

    er = er0;
    er.es_TextFormat = fmt;
    er.es_GadgetFormat = buttons;
    va_start(va, buttons);
      return EasyRequestArgs(PrefsWnd, &er, NULL, va);
    va_end(va)          /* produces error if it doesn't expand to empty */
}

void
DeselectUnit(void)
{
    if (IOReq->iotd_Req.io_Unit != NULL &&
	IOReq->iotd_Req.io_Unit != (void *)-1)
	CloseDevice((struct IORequest *)IOReq);

    IOReq->iotd_Req.io_Unit = NULL;
    Unit = NULL;
}

void
UpdateGadgets(void)
{
    SetGadgetAttr(GDX_UNIT, GTSL_Level, DevUnit);
    SetGadgetAttr(GDX_MAXCACHE, GTSL_Level, Settings.MaxCache);
    SetGadgetAttr(GDX_CMDUPDATE, GTCB_Checked, Settings.CacheCMDUPDATE);
    SetGadgetAttr(GDX_DELAY, GTCB_Checked, Settings.CacheDELAY);
    SetGadgetAttr(GDX_SAFE, GTCB_Checked, Settings.CacheSAFE);
    SetGadgetAttr(GDX_TIME, GTSL_Level, Settings.CacheTimeout);
    SetGadgetAttr(GDX_METHOD, GTST_String, (ULONG)Settings.XPKPackMethod);
}

void
ConfigFromUnit(UNIT *unit)
{
    Settings.MaxCache = unit->xu_MaxCache;
    Settings.CacheFlags = unit->xu_CacheFlags;
    Settings.CacheCMDUPDATE = (Settings.CacheFlags & CACHEF_CMDUPDATE) != 0;
    Settings.CacheDELAY     = (Settings.CacheFlags & CACHEF_DELAY) != 0;
    Settings.CacheSAFE	    = (Settings.CacheFlags & CACHEF_SAFEWRITE) != 0;
    Settings.Licensed	    = (Settings.CacheFlags & CACHEF_LICENSED) != 0;
    Settings.CacheTimeout = unit->xu_CacheTimeout;
    strcpy(Settings.XPKPackMethod, unit->xu_XPKPackMethod);

    UpdateGadgets();
}

void
SelectUnit(int unit)
{
    if (Unit && unit == DevUnit)
	return;

    DeselectUnit();

    DevUnit = unit;
    if (OpenDevice(DevName, unit, (struct IORequest *)IOReq, 0) != 0) {
	DeselectUnit();
	Confirm("Cannot open unit %ld", OkString, unit);
	return;
    }

    /* Make sure unit has initialised itself */
    IOReq->iotd_Req.io_Command = CMD_UPDATE;
    DoIO((struct IORequest *)IOReq);

    Unit = (struct XpkDiskUnit *)IOReq->iotd_Req.io_Unit;
    ConfigFromUnit(Unit);
    Initial = Settings;
}

int
Save(char *filename, int notify)
{
    char	    buffer[160];
    BPTR	    fh;

    sprintf(buffer, filename, DevUnit);

    if (fh = Open(buffer, MODE_NEWFILE)) {
	sprintf(buffer, S_CMDUPDATE "=%d,"
			S_DELAY "=%d,"
			S_SAFEWRITE "=%d,"
			S_MaxCache "=%d,"
			S_CacheTimeout "=%d,"
			S_XPKPackMethod "=%s,"
			S_LICENSED "=%d",
		Settings.CacheCMDUPDATE,
		Settings.CacheDELAY,
		Settings.CacheSAFE,
		Settings.MaxCache,
		Settings.CacheTimeout,
		Settings.XPKPackMethod,
		Settings.Licensed);
	Write(fh, buffer, strlen(buffer));
	Close(fh);

	if (notify && Unit) {
	    IOReq->iotd_Req.io_Command = CMD_RESET;
	    DoIO((struct IORequest *)IOReq);
	}
	return 1;
    }

    Confirm("Error writing \"%s\"", OkString, buffer);
    return 0;
}

void
DoSaveAs(void)
{
    struct FileRequester *fr;

    if (FileRequest == NULL) {
	FileRequest = (struct FileRequester *)
		      AllocAslRequest(ASL_FileRequest, NULL);
    }

    if (fr = FileRequest) {
	unsigned char	buffer[256];

	if (AslRequestTags(fr,
			    ASL_Hail, "Save settings as",
			    ASL_OKText, "Save",
			    ASL_FuncFlags, FILF_SAVE,
			    TAG_DONE
			  ) == 0) {
	    goto end;
	}
	strncpy(buffer, fr->rf_Dir, sizeof (buffer) - 1);
	AddPart(buffer, fr->rf_File, sizeof (buffer) - 1);

	Save(buffer, 0);
    end:
    }
}

void
DoOpen(int ask)
{
    struct FileRequester *fr;
    static	    int Ask;

    if (FileRequest == NULL) {
	FileRequest = (struct FileRequester *)
		      AllocAslRequest(ASL_FileRequest, NULL);
	ask = 1;
    }

    ask |= Ask;

    if (fr = FileRequest) {
	unsigned char	buffer[256];
	UNIT		unit;

	if (ask &&
	    AslRequestTags(fr,
			    ASL_Hail, "Load settings",
			    ASL_OKText, "Load",
			    ASL_FuncFlags, 0,
			    TAG_DONE
			  ) == 0) {
	    Ask = 1;
	    goto end;
	}
	strncpy(buffer, fr->rf_Dir, sizeof (buffer) - 1);
	AddPart(buffer, fr->rf_File, sizeof (buffer) - 1);
	Ask = 0;

	memset(&unit, 0, sizeof(unit));
	/*
	 * I admit, this is roundabout, but saves duplicating the
	 * parsing function by using the same code as the device.
	 */
	ReadConfig(&unit, buffer, sizeof(buffer));
	ConfigFromUnit(&unit);
    end: ;
    }
}

int
DoAbout(void)
{
    struct EasyStruct er = {
	sizeof er, 0, "About...",
"xpkdisk.device \xA9 1995 by Olaf Seibert.\n"
"\n"
"  This program is distributed in the hope that it will be useful,\n"
"  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n"
"See documentation and GNU General Public License for distribution.",
		    "Accept License|Reject License"
    };

    if (EasyRequestArgs(PrefsWnd, &er, NULL, NULL) == 0) {
	return DONE_DONE;
    }
    Settings.Licensed = 1;
    return 0;
}

void
MaybeAbout(void)
{
    if (!Settings.Licensed && DoAbout())
	Die();
}

int
DoGadget(struct IntuiMessage *imsg)
{
    struct Gadget  *gd;
    int 	    id;

    gd = (struct Gadget *) imsg->IAddress;
    if (gd == NULL)
	return 0;
    id = gd->GadgetID;

    switch (id) {
    case GD_UNIT:
	SelectUnit(imsg->Code);
	break;
    case GD_MAXCACHE:
	Settings.MaxCache = imsg->Code;
	break;
    case GD_CMDUPDATE:
	Settings.CacheCMDUPDATE = (gd->Flags & GFLG_SELECTED) != 0;
	break;
    case GD_DELAY:
	Settings.CacheDELAY = (gd->Flags & GFLG_SELECTED) != 0;
	break;
    case GD_SAFE:
	Settings.CacheSAFE = (gd->Flags & GFLG_SELECTED) != 0;
	break;
    case GD_TIME:
	Settings.CacheTimeout = imsg->Code;
	break;
    case GD_METHOD:
	strncpy(Settings.XPKPackMethod, GetString(gd), 9);
	break;
    case GD_SAVE:
	Save(EnvArc, FALSE);
	/* fall through: SAVE implies USE. */
    case GD_USE:
	Save(Env, TRUE);
	break;
    case GD_QUIT:
	return DONE_DONE;
    }

    return 0;
}

int
DoMenu(struct IntuiMessage *imsg)
{
    int 	    menu = MENUNUM(imsg->Code);
    int 	    item = ITEMNUM(imsg->Code);

    switch (menu) {
    case 0:	    /* Project */
	switch (item) {
	case 0:     /* - About... */
	    return DoAbout();
	case 1:     /* - Open... */
	    DoOpen(1);
	    break;
	case 2:     /* - Save */
	    Save(EnvArc, FALSE);
	    goto use;
	case 3:     /* - Save As... */
	    DoSaveAs();
	    break;
	case 4:     /* - Use */
	use:
	    Save(Env, TRUE);
	    break;
	case 6:     /* - Quit */
	    return DONE_DONE;
	}
	break;
    case 1:	    /* Edit */
	switch (item) {
	case 0:     /* - Reset to defaults */
	    Settings = Defaults;
	    UpdateGadgets();
	    break;
	case 1:     /* - Last saved */
	    DoOpen(0);
	    break;
	case 2:     /* - Restore */
	    Settings = Initial;
	    UpdateGadgets();
	    break;
	}
	break;
    }
    return 0;
}

void
MainLoop(void)
{
    long	    waitmask;
    long	    mainmask;
    long	    mask;
    int 	    done;

    done = 0;

    if (PrefsWnd)
	mainmask = 1L << PrefsWnd->UserPort->mp_SigBit;
    else
	mainmask = 0;

    waitmask = mainmask | SIGBREAKF_CTRL_C;

    while (!done) {
	mask = Wait(waitmask);
	if (mask & SIGBREAKF_CTRL_C) {
	    done = DONE_DONE;
	    break;
	}
	if (mask & mainmask) {
	    struct IntuiMessage *imsg;

	    while (imsg = GT_GetIMsg(PrefsWnd->UserPort)) {
		switch (imsg->Class) {
		case IDCMP_CLOSEWINDOW:
		    done = DONE_DONE;
		    break;
		case IDCMP_GADGETDOWN:
		case IDCMP_GADGETUP:
		case IDCMP_MOUSEMOVE:
		    done = DoGadget(imsg);
		    break;
		case IDCMP_MENUPICK:
		    done = DoMenu(imsg);
		    break;
		case IDCMP_REFRESHWINDOW:
		    GT_BeginRefresh(imsg->IDCMPWindow);
		    PrefsRender();
		    GT_EndRefresh(imsg->IDCMPWindow, TRUE);
		    break;
		}
		GT_ReplyIMsg(imsg);
	    }
	}
    }
}

void chkabort(void) {}      /* DICE specific. */

int
main(int argc, char **argv)
{
    OpenAll(argc, argv);
    MaybeAbout();
    MainLoop();
    Die();

    return 0;
}

#ifndef WORKBENCH_STARTUP_H
#include <workbench/startup.h>
#endif

int
wbmain(struct WBStartup *wbs)
{
    int 	    rc;
    long	    lock;

    if (wbs->sm_ArgList)
	lock = CurrentDir(wbs->sm_ArgList->wa_Lock);
    rc = main(0, (char **)wbs);
    if (wbs->sm_ArgList)
	CurrentDir(lock);
    return rc;
}
