/*
 *  MXReq.c
 *  ~~~~~~~
 *  simple mutual exclude requester, esp. useful for script files
 *
 *  © 1992 by a.weißflog, member of >46<
 *
 *  created:	6-Sep-92    V0.99
 *  updates:	7-Sep-92    V1.00 -> bugfixes
 *		9-Sep-92    V1.01 -> uses no longer fixed topaz font,
 *				     now using GfxBase->DefaultFont
 *	       10-Sep-92    V1.02 -> minimum window width added
 *				     cancels if windowsize > screensize;
 *				     (does it ?)
 *	       12-Sep-92    V1.10 -> major revision: replaced STDOUT-Errors
 *				     by EasyReq()'s, added About-Requester
 *				     on RMB
 *	       13-Sep-92    V1.20 -> added optional help-requester.
 *	       03-Dec-92    ----- -> fixed hidden bug in helpfile-
 *				     memory allocation
 */
#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <dos/var.h>
#include <graphics/text.h>
#include <graphics/gfxbase.h>
#include <devices/inputevent.h>
#include <string.h>

#define VERSION      "V1.20"
#define CYEAR	     "1992"
#define CDATE	     "13.09.1992"
#define ERRORTITLE   "MXReq Error:"

#define BUFSIZE      512
#define RADIOBUTTON  1
#define OKBUTTON     2
#define CANCELBUTTON 3
#define HELPBUTTON   4
#define BETWEENH     22
#define GADSPACEW    8	    /* x-space between button gadgets */

struct DOSArgs {
		char *title;
		char *str1;
		char *str2;
		char *str3;
		char *str4;
		char *str5;
		char *str6;
		char *str7;
		char *str8;
		LONG neverused; /* this is a 'dummy' parameter used as */
		char *env1;	/* terminator of the string pointer array */
		char *env2;
		char *env3;
		char *env4;
		char *env5;
		char *env6;
		char *env7;
		char *env8;
		char *envname;
		char *helpfile;
	       } DOSArgs;

char TemplateStr[] = "TITLE/K,STR1/A,STR2/A,STR3/K,STR4/K,"\
		     "STR5/K,STR6/K,STR7/K,STR8/K,NEVERUSE/S,ENV1/K,ENV2/K,ENV3/K,"\
		     "ENV4/K,ENV5/K,ENV6/K,ENV7/K,ENV8/K,ENVNAME/K,HELPFILE/K";
struct RDArgs *ArgumentPtr;

char VString[]	  =  "$VER: MXReq " VERSION " (" CDATE ")";

int TitleLen  = 0;
int MaxStrLen = 0;
int NumStr    = 0;

struct Screen *PubScreen;
struct Window *Win;
struct Gadget *gad;
struct Gadget *glist = NULL;
void *VI;

struct NewGadget ng;
struct TextAttr TA = { "topaz.font", 8, 0, 0 };     /* will be overwritten */

int UserChoice = 0;

/* EasyReq structures */
struct EasyStruct LibES   = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Sorry, couldn't find %s V37.",
			    "Retry|Cancel"
			    };

struct EasyStruct GadgES  = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Sorry, CreateGadget failed.\nMXReq now terminating.",
			    "OK"
			    };

struct EasyStruct WinES   = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Sorry, OpenWindow failed.\nMXReq now terminating.",
			    "OK"
			    };

struct EasyStruct EnvES   = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Sorry, couldn't create environment\nvariable.",
			    "Retry|Cancel"
			    };

struct EasyStruct GenES   = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Very serious error:\n%s.\nMXReq now terminating.",
			    "OK"
			    };

struct EasyStruct RMBES   = {
			    sizeof(struct EasyStruct), 0,
			    "About MXReq:",
			    "MXReq "VERSION"\n\n© "CYEAR" by A.Weißflog\n\n"\
			    "This program is PUBLIC DOMAIN and\n"\
			    "another fine product of >46<.",
			    "OK"
			    };

struct EasyStruct HelpES  = {
			    sizeof(struct EasyStruct), 0,
			    "Help Requester:",
			    0,
			    "OK"
			    };

struct EasyStruct DOSES   = {
			    sizeof(struct EasyStruct), 0,
			    ERRORTITLE,
			    "Sorry, couldn't find your helpfile:\n%s",
			    "OK"
			    };

/* library definitions */
struct Library *IntuitionBase;
struct Library *GadToolsBase;
struct GfxBase	 *GfxBase;

char *IntuitionName = "intuition.library";
char *GadToolsName = "gadtools.library";
char *GfxName = "graphics.library";

/*
 *  BOOL OpenLibs()
 *  ~~~~~~~~~~~~~~~
 *  opens libraries, puts() errors to stdout or EasyRequest()
 *
 *  BOOL = TRUE  -> ok
 *	 = FALSE -> couldn't open a lib
 */
BOOL OpenLibs()
{
    int answer = 1;

    IntuitionBase = (struct Library *)
		    OpenLibrary (IntuitionName, 37L);

    if (!IntuitionBase)
    {
	puts("MXReq requires Kickstart 2.0 or later !!!");
	return(FALSE);
    };

    while (!(GadToolsBase = (struct Library *)
			    OpenLibrary(GadToolsName, 37L)) && answer)
    {
	answer = EasyRequest(NULL, &LibES, NULL, GadToolsName);
    };
    if (!answer)    return(FALSE);

    while (!(GfxBase = (struct Library *)
		       OpenLibrary(GfxName, 37L)) && answer)
    {
	answer = EasyRequest(NULL, &LibES, NULL, GfxName);
    };
    if (!answer)    return(FALSE);

    return(TRUE);
};

/*
 *  void CloseLibs()
 *  ~~~~~~~~~~~~~~~~
 *  closes all opened libs
 */
CloseLibs()
{
    if (GfxBase)            CloseLibrary (GfxBase);
    if (GadToolsBase)       CloseLibrary (GadToolsBase);
    if (IntuitionBase)      CloseLibrary (IntuitionBase);
};

/*
 *  BOOL GetDOSArgs()
 *  ~~~~~~~~~~~~~~~~~
 *  reads line parameters via ReadArgs()
 *
 *  BOOL = TRUE  -> OK
 *	 = FALSE -> error reading args (a copyright string was
 *		    sent to STDOUT)
 */
BOOL GetArguments()
{
    ArgumentPtr = (struct RDArgs *) ReadArgs (TemplateStr, &DOSArgs, NULL);
    if (!ArgumentPtr)
    {
	EasyRequest(NULL, &GenES, NULL, "Wrong parameter string");
	return(FALSE);
    }
    else
    {
	DOSArgs.neverused = NULL;   /* terminator of string pointer array */
	if (!DOSArgs.title)  DOSArgs.title = "MXReq "VERSION;
	return(TRUE);
    };
};

/*
 *  void FreeArguments()
 *  ~~~~~~~~~~~~~~~~~~~~
 *  frees args
 */
FreeArguments()
{
    if (ArgumentPtr) FreeArgs(ArgumentPtr);
};

/*
 *  void GetMaxStrNumStr()
 *  ~~~~~~~~~~~~~~~~~~~~~~
 *  get the length of the title and the max length of
 *  the menu entries in chars
 */
GetMaxStrNumStr()
{
    int DummyLen;

    TitleLen = strlen(DOSArgs.title);

    if (DOSArgs.str1)
    {
	NumStr +=1;
	DummyLen = strlen(DOSArgs.str1);
	if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
	if (DOSArgs.str2)
	{
	    NumStr+=1;
	    DummyLen = strlen(DOSArgs.str2);
	    if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
	    if (DOSArgs.str3)
	    {
		NumStr+=1;
		DummyLen = strlen(DOSArgs.str3);
		if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
		if (DOSArgs.str4)
		{
		    NumStr+=1;
		    DummyLen = strlen(DOSArgs.str4);
		    if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
		    if (DOSArgs.str5)
		    {
			NumStr+=1;
			DummyLen = strlen(DOSArgs.str5);
			if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
			if (DOSArgs.str6)
			{
			    NumStr+=1;
			    DummyLen = strlen(DOSArgs.str6);
			    if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
			    if (DOSArgs.str7)
			    {
				NumStr+=1;
				DummyLen = strlen(DOSArgs.str7);
				if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
				if (DOSArgs.str8)
				{
				    NumStr+=1;
				    DummyLen = strlen(DOSArgs.str8);
				    if (DummyLen>MaxStrLen)  MaxStrLen = DummyLen;
				};
			    };
			};
		    };
		};
	    };
	};
    };
};

/*
 *  BOOL OpenIntuitionShit()
 *  ~~~~~~~~~~~~~~~~~~~~~~~~
 *  locks pubscreen, opens window and gadgets, errors
 *  will be puts()'ed to STDOUT or EasyRequest()
 *
 *  BOOL = True  -> ok
 *	 = FALSE -> something went wrong
 */
BOOL OpenIntuitionShit()
{
    struct TextFont *deffont;
    int winWidth, winHeight, winTop, winLeft;

    /* get pubscreen */
    PubScreen	  = (struct Screen *)  LockPubScreen (NULL);
    if (!PubScreen)
    {
	EasyRequest(NULL, &GenES, NULL, "Couldn't lock pubscreen");
	return(FALSE);
    };

    /* do the font stuff */
    /* get length of title string in pixels */
    int titlePixLen = TextLength(&PubScreen->RastPort,
				 DOSArgs.title,
				 TitleLen);

    /* set TextAttr */
    deffont = GfxBase->DefaultFont;

    TA.ta_Name	= deffont->tf_Message.mn_Node.ln_Name;
    int fHeight     = deffont->tf_YSize;
    TA.ta_YSize = fHeight;
    int fWidth	    = deffont->tf_XSize;

    /* get height of button gadgets */
    int gadHeight = fHeight + 4;
    int gadWidth  = 8 * fWidth;
    int minWinWidth = 3*gadWidth + 4*GADSPACEW;

    /* get window width & height & position */
    if (TitleLen>MaxStrLen)     winWidth = TitleLen*fWidth + 80;
    else winWidth = MaxStrLen*fWidth + 80;
    if (winWidth < minWinWidth) winWidth = minWinWidth; /* gadgets fit ? */
    if (winWidth > (PubScreen->Width))
    {
	EasyRequest(NULL, &GenES, NULL, "Requester won't fit on your screen");
	return(FALSE);
    };

    winHeight = NumStr*(fHeight+3)+gadHeight+PubScreen->Font->ta_YSize + BETWEENH;
    if (winHeight > (PubScreen->Height))
    {
	EasyRequest(NULL, &GenES, NULL, "Requester won't fit on your screen");
	return(FALSE);
    };

    /* pos OK gadget under mousebutton */
    winTop    = (PubScreen->MouseY) - winHeight + 10;
    if (winTop<0) winTop=0;
    if ((winTop+winHeight) > (PubScreen->Height))
	winTop = PubScreen->Height - winHeight;

    winLeft   = (PubScreen->MouseX) - winWidth/4;
    if (winLeft<0) winLeft=0;
    if ((winLeft+winWidth) > (PubScreen->Width))
	winLeft = PubScreen->Width - winWidth;


    /* get visual info */
    VI		  = (void *)    GetVisualInfo (PubScreen, TAG_END);
    if (!VI)
    {
	EasyRequest(NULL, &GenES, NULL, "Couldn't get visual info");
	return(FALSE);
    };

    /* create gadget list */
    gad 	  = (struct Gadget *)   CreateContext(&glist);
    if (!gad)
    {
	EasyRequest(NULL, &GenES, NULL, "Couldn't create gadget list");
	return(FALSE);
    };

    ng.ng_TextAttr	= &TA;
    ng.ng_VisualInfo	= VI;
    ng.ng_LeftEdge	= (winWidth-MaxStrLen*fWidth)/2 - 10;  /* zentriert */
    ng.ng_TopEdge	= PubScreen->Font->ta_YSize + 8;
    ng.ng_GadgetID	= RADIOBUTTON;
    ng.ng_Flags 	= PLACETEXT_RIGHT;

    gad 	  = (struct Gadget *)
		    CreateGadget (MX_KIND, gad, &ng,
				  GTMX_Labels,	&DOSArgs.str1,
				  GTMX_Spacing, 3,
				  TAG_END);
    if (!gad)
    {
	EasyRequest(NULL, &GadgES, NULL);
	return(FALSE);
    };

    ng.ng_LeftEdge	= 8;
    ng.ng_TopEdge	= winHeight-gadHeight-3;
    ng.ng_Width 	= gadWidth;
    ng.ng_Height	= gadHeight;
    ng.ng_GadgetText	= "OK";
    ng.ng_GadgetID	= OKBUTTON;
    ng.ng_Flags 	= PLACETEXT_IN;

    gad   = (struct Gadget *)
	    CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
    if (!gad)
    {
	EasyRequest(NULL, &GadgES, NULL);
	return(FALSE);
    };

    ng.ng_LeftEdge	= winWidth - gadWidth - GADSPACEW;
    ng.ng_GadgetText	= "CANCEL";
    ng.ng_GadgetID	= CANCELBUTTON;

    gad   = (struct Gadget *)
	    CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
    if (!gad)
    {
	EasyRequest(NULL, &GadgES, NULL);
	return(FALSE);
    };

    if (DOSArgs.helpfile)
    {
	ng.ng_LeftEdge	    = winWidth/2 - gadWidth/2;
	ng.ng_GadgetText    = "HELP";
	ng.ng_GadgetID	    = HELPBUTTON;

	gad = (struct Gadget *)
	      CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
	if (!gad)
	{
	    EasyRequest(NULL, &GadgES, NULL);
	    return(FALSE);
	};
    };

    /* try to open window */
    Win 	  = (struct Window *)
		    OpenWindowTags (NULL,
			WA_Title,	DOSArgs.title,
			WA_Gadgets,	glist,
			WA_Top, 	winTop,
			WA_Left,	winLeft,
			WA_Width,	winWidth,
			WA_Height,	winHeight,
			WA_DragBar,	TRUE,
			WA_DepthGadget, TRUE,
			WA_Activate,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_IDCMP,	IDCMP_REFRESHWINDOW|
					MXIDCMP|
					BUTTONIDCMP|
					IDCMP_MOUSEBUTTONS,
			WA_PubScreen,	PubScreen,
			TAG_END);
    if (!Win)
    {
	EasyRequest(NULL, &WinES, NULL);
	return(FALSE);
    };

    GT_RefreshWindow (Win, NULL);
    return(TRUE);
};

/*
 *  void CloseIntuitionShit()
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~
 *  closes all intuition objects
 */
CloseIntuitionShit()
{
    if (Win)            CloseWindow(Win);
    if (gad)            FreeGadgets(glist);
    if (VI)             FreeVisualInfo(VI);
    if (PubScreen)      UnlockPubScreen(NULL, PubScreen);
};

/*
 *  BOOL ShowHelp()
 *  ~~~~~~~~~~~~~~~
 *  will do the following things:
 *	-> alloc filebuffer
 *	-> read the helpfile into the filebuffer and convert
 *	   [NEWLINES] into '\n'
 *	-> show an EasyReq with a pointer to the helptext
 *  if something went wrong ShowHelp will return FALSE
 */
BOOL ShowHelp()
{
    char *Buffer;   /* pointer to the help text later included */
    BPTR File;	    /* the filepointer of the help file */
    char Char;
    int  BufPos = 0;
    BOOL ret = TRUE;

    if ((Buffer = (char *) AllocMem(BUFSIZE+4, MEMF_PUBLIC|MEMF_CLEAR)))
    {
	if ((File = Open(DOSArgs.helpfile, MODE_OLDFILE)))
	{
	    while (((Char = FGetC(File)) != -1) && (BufPos<BUFSIZE))
	    {
		if (Char == '%')  Char = '*';

		Buffer[BufPos] = Char;
		BufPos +=1;
	    };
	    Close(File);
	    Buffer[BufPos] = 0;  /* set terminator */
	    HelpES.es_TextFormat = Buffer;
	    EasyRequest(NULL, &HelpES, NULL);
	}
	else
	{
	    EasyRequest(NULL, &DOSES, NULL, DOSArgs.helpfile);
	    ret = FALSE;
	};
	FreeMem(Buffer, BUFSIZE+4);
    }
    else
    {
	EasyRequest(NULL, &GenES, NULL, "Not enough memory for helpfile !");
	ret = FALSE;
    };
    return(ret);
};



/*
 *  BOOL Dispatcher()
 *  ~~~~~~~~~~~~~~~~~
 *  waits for incoming messages an does the handle
 *
 *  BOOL = TRUE ->  OK gadget pressed
 *	 = FALSE -> CANCEL gadget pressed
 */
BOOL Dispatcher()
{
    struct IntuiMessage *imsg;
    struct Gadget *gaddy;
    BOOL ret   = TRUE;
    BOOL ready = FALSE;

    while (!ready)
    {
	Wait (1 << Win->UserPort->mp_SigBit);

	while ((!ready) && (imsg = (struct IntuiMessage *)
				   GT_GetIMsg (Win->UserPort)))
	{
	    switch (imsg->Class)
	    {
		case IDCMP_REFRESHWINDOW:
		    GT_BeginRefresh(Win);
		    GT_EndRefresh(Win, TRUE);
		    break;
		case MXIDCMP:
		    UserChoice = (int) imsg -> Code;
		    break;
		case BUTTONIDCMP:
		    gaddy = imsg->IAddress;
		    if (gaddy->GadgetID == OKBUTTON)      { ret=TRUE;
							    ready=TRUE;
							    break;
							  };
		    if (gaddy->GadgetID == CANCELBUTTON)  { ret=FALSE;
							    ready=TRUE;
							    break;
							  };
		    if (gaddy->GadgetID == HELPBUTTON)    {
							  ShowHelp();
							  break;
							  };
		    break;
		case IDCMP_MOUSEBUTTONS:
		    if (imsg->Code == MENUDOWN)
			EasyRequest(Win, &RMBES, NULL);
		    break;
	    };
	    GT_ReplyIMsg(imsg);
	};
    };
    return(ret);
};

/*
 *  void WriteEnvVar(char *EnvValue, char *AltValue)
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *  creates an env variable with the name in DOSArgs.envname
 *  (alternate: MXREQ)
 *
 *  char *EnvValue   value of the env variable
 *  char *AltValue   alt value if EnvValue points to a null string
 */
WriteEnvVar(char *EnvValue, char *AltValue)
{
    char *EnvName = DOSArgs.envname;

    if (!EnvName)    EnvName = "MXREQ";
    if (!EnvValue)   EnvValue= AltValue;

    BOOL answer = 1;
    while (!(SetVar (EnvName, EnvValue, -1, GVF_GLOBAL_ONLY)) && answer)
    {
	answer = EasyRequest(NULL, &EnvES, NULL);
    };
};

/******************************
**			     **
**  THE MAIN ROUTINE ITSELF  **
**			     **
******************************/
main()
{
    int ret = 0;

    if (OpenLibs())
    {
	if (GetArguments())
	{
	    GetMaxStrNumStr();
	    if (OpenIntuitionShit())   /* Window, Gadgets... */
	    {
		if (Dispatcher())
		{
		    switch (UserChoice)
		    {
			case 0: WriteEnvVar(DOSArgs.env1,"1"); break;
			case 1: WriteEnvVar(DOSArgs.env2,"2"); break;
			case 2: WriteEnvVar(DOSArgs.env3,"3"); break;
			case 3: WriteEnvVar(DOSArgs.env4,"4"); break;
			case 4: WriteEnvVar(DOSArgs.env5,"5"); break;
			case 5: WriteEnvVar(DOSArgs.env6,"6"); break;
			case 6: WriteEnvVar(DOSArgs.env7,"7"); break;
			case 7: WriteEnvVar(DOSArgs.env8,"8"); break;
		    };
		}
		else ret = 10;	/* CANCELed */
	    }
	    else ret = 20;  /* OpenIntuitionShit failed */
	    CloseIntuitionShit();
	}
	else ret = 20;	/* ReadArgs() failed */
	FreeArguments();
    }
    else ret = 20; /* if OpenLibs() failed */
    CloseLibs();
    exit(ret);
};
