/*
 * ticker.c
 *
 * Test application for AppShell Timer Handler
 * Written by John F Wiederhirn
 *
 * Based heavily on code in skeleton.c
 * Copyright (C) 1991 Commodore-Amiga, Inc.
 * Written by David N. Junod
 *
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <libraries/gadtools.h>
#include <libraries/appshell.h>
#include <utility/tagitem.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/appshell_protos.h>
#include <clib/utility_protos.h>
#include <pragmas/appshell.h>
#include <string.h>

#include "timer.h"
#include "ticker_rev.h"

/* The AppShell maintains some basic information on the application.  This
 * information would appear in the About requester, and in AppExchange. */
#define	APPBASE "TICKER"
#define	APPNAME "Ticker"
#define	APPVERS	VERS
#define	APPCOPY "Copyright (C) 1991 Kompositor Software"
#define	APPAUTH "John F Wiederhirn"

STRPTR ver = VERSTAG;

/* This structure contains all the data that our application will need.
 * Change to suit your application. */
struct AppData
{
    UBYTE ad_TmpText[128];	/* Temporary text buffer */
    LONG ad_Mode;		/* Output mode */
};

/* Each public function gets a numeric ID assigned to it.  You must
 * assign an ID to each one of your functions.  Look at appshell.h for
 * standard function ID's for things like New, Open, Cut, Copy, Paste,
 * Quit, etc... */
#define DUMMYID		APSH_USER_ID
#define CInitID		(DUMMYID + 1L)
#define	OpenMainID	(DUMMYID + 2L)
#define	SetModeID	(DUMMYID + 3L)
#define	ErrorID		(DUMMYID + 4L)
#define TickID		(DUMMYID + 5L)
#define BeepID		(DUMMYID + 6L)
#define	GetInfoID	(DUMMYID + 7L)
#define LAST_ID		(DUMMYID + 8L)


/* These are the function prototypes for all the application implemented
 * functions used in this example.  You must prototype all the functions
 * that your application is going use in the function table. */
VOID CInitFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID OpenMainFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID SetModeFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID GetInfoFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID ErrorFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID TickFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID BeepFunc (struct Hook *, struct AppInfo *, struct AppFunction *);
VOID QuitFunc (struct Hook *, struct AppInfo *, struct AppFunction *);

/* The AppShell will convert this array into function table entries and will
 * add them to the function table list.  Using the APSHF_PRIVATE flag, we are
 * able to have functions that can't be triggered by the user.  This
 * also defines the commands that are available through ARexx.  Note that
 * the command parser is case-INSENSITIVE, but for readability, your
 * entries in this table should follow standard capitalization rules. */
struct Funcs FTable[] =
{
    {"Quit", QuitFunc, QuitID,},
    {"Error", ErrorFunc, ErrorID,},

	// This is my baby...it handles incoming tick messages
    {"Tick", TickFunc, TickID,},
    {"Beep", BeepFunc, BeepID,},

 /* Functions with an argument template */
    {"SetMode", SetModeFunc, SetModeID, "MALE/S,FEMALE/S,ZOMBIE/S", 3L, NULL,},
    {"GetInfo", GetInfoFunc, GetInfoID, "STEM", 1L, NULL},

 /* These functions are private, and can not be accessed by the user */
    {"CInit", CInitFunc, CInitID, NULL, NULL, APSHF_PRIVATE},
    {"OpenMain", OpenMainFunc, OpenMainID, NULL, NULL, APSHF_PRIVATE},

 /* Marks the end of the array */
    {NULL, NO_FUNCTION,}
};

/* All text used in the application must be defined in a text array.  Then
 * referred to by numeric ID. */
STRPTR Def_Text[] =
{
 /* Padding */
    "Written by John F Wiederhirn",

 /* Window title */
    "The Ticker",
#define	TEXT_TITLE	1

    "Sex:",
#define	TEXT_LABEL	2

 /* Main window menus */
#define	TEXT_MENU	3
    " TITLE PROJECT  LABEL Project",
    "  ITEM QUIT     LABEL Quit                 KEY Q CMD Quit",

    " TITLE EXTRAS   LABEL Extras",
    "  ITEM ERROR    LABEL \"Show Error...\"    KEY , CMD Error",
    "  ITEM CMDSHELL LABEL \"Command Shell...\" KEY . CMD CmdShell",

    "   END",

    "Error: %s",
#define	ERROR_DISPLAY	9

    "Can't send stem variable",
#define	ERROR_CANT_RVI	10

 /* NULL termination is required */
    NULL
};


/* The following four tag arrays are GadTool tags for Window Environment
 * objects. */
STRPTR output_data[] =
{
    "Male",
    "Female",
    "Zombie",
    NULL
};

struct TagItem output_tags[] =
{
    {GTCY_Labels, (LONG) output_data},
    {TAG_DONE,}
};

/* These are the objects that are going to be within our main window.
 * Note that the window itself is an object, and receives the SAME name
 * as in the WindowEnvironment tag list, APSH_NameTag.  If you want
 * to specify any window tags, attach them to the window object. */
struct Object objects[] =
{
    {&objects[1], 0, 0, OBJ_Window, NULL, NULL, NULL, "Main", TEXT_TITLE,
     {0, 0, 0, 0}, NULL,},

    {NULL, 0, 0, OBJ_Cycle, SetModeID, NULL, NULL, "Mode", TEXT_LABEL,
     {44, 4, 101, 16}, output_tags,},
};

/* This is a Window Environment tag list.  It describes a window. */
struct TagItem mainenv[] =
{
    {APSH_NameTag, (ULONG) "Main"},
    {APSH_Objects, (ULONG) objects},
    {APSH_TTMenu, TEXT_MENU},
    {APSH_WinAOpen, OpenMainID},
    {APSH_RefreshData, OpenMainID},
    {APSH_CloseWindow, QuitID},
    {TAG_DONE,}
};

/* Shared system libraries */
extern struct Library *SysBase;
extern struct Library *DOSBase;
struct Library *GadToolsBase;
struct Library *GfxBase;
struct Library *IconBase;
struct Library *IntuitionBase;
struct Library *UtilityBase;
struct Library *AppObjectsBase;

/* Library Environment:
 * This tag array is used to open and close the shared system libraries
 * needed by our application.
 */
struct TagItem Our_Libs[] =
{
 /* Minimum library version */
    {APSH_LibVersion, 36L},

 /* All libraries are required */
    {APSH_LibStatus, APSH_REQUIRED},

 /* Libraries to open */
    {APSH_GadTools, (ULONG) & GadToolsBase},
    {APSH_Gfx, (ULONG) & GfxBase},
    {APSH_Icon, (ULONG) & IconBase},
    {APSH_Intuition, (ULONG) & IntuitionBase},
    {APSH_Utility, (ULONG) & UtilityBase},
    {APSH_AppObjects, (ULONG) & AppObjectsBase},
    {TAG_DONE,}
};

/* Shell argument template */
#define TEMPLATE "Male/S,Female/S,Zombie/S,PubScreen/K,PortName/K,Startup/K,NOGUI/S"
#define OPT_MALE	0
#define OPT_FEMALE	1
#define	OPT_ZOMBIE	2
#define	OPT_SCREENNAME	3
#define	OPT_PORTNAME	4
#define	OPT_STARTUP	5
#define	OPT_NOGUI	6
#define OPT_COUNT	7

 /* ARexx user interface environment specification array */
struct TagItem Handle_AREXX[] =
{
    {APSH_Extens, (ULONG) "skel"},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

 /* Command Shell user interface environment specification array */
struct TagItem Handle_DOS[] =
{

 /*
  * Presence of this tag, makes the Command Shell to stay closed until the user
  * sends a CMDSHELL OPEN command.
  */
    {APSH_Status, APSHP_INACTIVE},
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

/* These tags describe the Intuition user interface. */
struct TagItem Handle_IDCMP[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

/* These tags describe the Simple IPC user interface. */
struct TagItem Handle_SIPC[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

// FOR TIMER HANDLER
//
// This declares the delay value.
// Extend it if you need more time before it all blows up.
//

/* This is a timer test */
struct TagItem Handle_TIMER[] =
{
	{APSH_Setup, setup_timerA},
	{APSH_Rating, APSH_REQUIRED},
	{APSH_Status, APSHP_INACTIVE},
	{TAG_DONE,}
};

/* Application Environment:
 * Tell about our application */
struct TagItem Our_App[] =
{
 /* About the application */
    {APSH_AppName, (ULONG) APPNAME},
    {APSH_AppVersion, (ULONG) APPVERS},
    {APSH_AppCopyright, (ULONG) APPCOPY},
    {APSH_AppAuthor, (ULONG) APPAUTH},

 /* Trigger the library opening module */
    {APSH_OpenLibraries, (ULONG) Our_Libs},

 /* Specify the application function table */
    {APSH_FuncTable, (ULONG) FTable},

 /* Specify the application text table */
    {APSH_DefText, (ULONG) Def_Text},

 /* Tell how memory we need for our own data */
    {APSH_UserDataSize, sizeof (struct AppData)},

 /* Give our Shell startup argument template */
    {APSH_Template, (ULONG) TEMPLATE},
    {APSH_NumOpts, (ULONG) OPT_COUNT},

 /* Must always specify the SIPC user interface */
    {APSH_AddSIPC_UI, (ULONG) Handle_SIPC},

 /* Add an ARexx user interface */
    {APSH_AddARexx_UI, (ULONG) Handle_AREXX},

 /* Add a Command Shell user interface */
    {APSH_AddCmdShell_UI, (ULONG) Handle_DOS},

 /* Add an Intuition user interface */
    {APSH_AddIntui_UI, (ULONG) Handle_IDCMP},

 /* Add a custom handler (timer) */
	{APSH_AddHandler, (ULONG) Handle_TIMER},

 /* Specify a custom initialization routine */
    {APSH_AppInit, CInitID},

    {TAG_DONE,}
};

extern struct Library *AppShellBase;

/* Sample initialization function */
VOID CInitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    extern struct TagItem mainenv[];
    struct AppData *ad = ai->ai_UserData;
    struct timeval tval5 = {5L, 0L};
    struct timeval tval1 = {1L, 0L};

    /* See if any Shell arguments were specified */
    if (ai->ai_Options[OPT_MALE])
    {
	ad->ad_Mode = 0L;
    }
    else if (ai->ai_Options[OPT_FEMALE])
    {
	ad->ad_Mode = 1L;
    }
    else if (ai->ai_Options[OPT_ZOMBIE])
    {
	ad->ad_Mode = 2L;
    }

    /* Open the Main window */
    HandlerFunc (ai,
		APSH_Handler, "IDCMP",
		APSH_Command, APSH_MH_OPEN,
		APSH_WindowEnv, (ULONG) mainenv,
		TAG_DONE);

    /* Here we start the timer ticking */
    HandlerFunc (ai,
		APSH_Handler,	"TIMER",		// Us
		APSH_Command,	APSH_MH_OPEN,		// Open us
		APSH_TimerMode,	TH_TICKING,		// Open us ticking
		APSH_TimerLen,	(ULONG) &tval5,		// Open us ticking for so long
		APSH_NameTag,	(ULONG) "Ticker5",	// and take our name.
		APSH_CmdID,	TickID,			// Wake me when its over...
		TAG_DONE);

    /* Here we start the timer ticking */
    HandlerFunc (ai,
		APSH_Handler,	"TIMER",		// Us
		APSH_Command,	APSH_MH_OPEN,		// Open us
		APSH_TimerMode,	TH_TICKING,		// Open us ticking
		APSH_TimerLen,	(ULONG) &tval1,		// Open us ticking for so long
		APSH_NameTag,	(ULONG) "Ticker1",	// and take our name.
		APSH_CmdID,	BeepID,			// Wake me when its over...
		TAG_DONE);
}

//
// If the dratted thing ever got this far, it should tick and switch from
// Male->Female->Zombie every n seconds as set in the timeval structure.
// Worked fine in the single-threaded one, here it doesnt even get this far.
//
VOID TickFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct Gadget *gad;
    struct Window *win;
    LONG mode = (-1);

    mode = ++ad->ad_Mode;
    if ((ad->ad_Mode)>2)
	ad->ad_Mode = mode = 0;

    /* Update the gadget */
    if ((mode >= 0) &&
    (APSHGetGadgetInfo (ai, "Main", "Mode", (ULONG *) & win, (ULONG *) & gad)))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL, GTCY_Active, mode, TAG_DONE);
    }

}

/* Just to show you that there can be multiple events occurring... */
VOID BeepFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    DisplayBeep (NULL);
}

/* This function is called every time the Main window is opened.  It
 * was specified by using the APSH_WinAOpen tag in the Main Window
 * Environemt tag array.  I'm also 'cheating' and calling it everytime
 * I want to refresh the window and graphics. */

VOID OpenMainFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct Window *win;
    struct Gadget *gad;

    /* Update the mode gadget */
    if (APSHGetGadgetInfo (ai, "Main", "Mode", (ULONG *) & win, (ULONG *) & gad))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL, GTCY_Active, ad->ad_Mode, TAG_DONE);
    }
}

VOID SetModeFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct TagItem *tag;
    struct Gadget *gad;
    struct Window *win;
    LONG mode = (-1);
    struct Funcs *f;

    if (f = af->af_FE)
    {
	if (f->fe_Options[0])
	{
	    ad->ad_Mode = mode = 0;
	}
	else if (f->fe_Options[1])
	{
	    ad->ad_Mode = mode = 1;
	}
	else if (f->fe_Options[2])
	{
	    ad->ad_Mode = mode = 2;
	}
    }
    /* Being set from the gadget */
    else if (tag = FindTagItem (APSH_MsgCode, attrs))
    {
	ad->ad_Mode = tag->ti_Data;
    }

    /* Update the gadget */
    if ((mode >= 0) &&
    (APSHGetGadgetInfo (ai, "Main", "Mode", (ULONG *) & win, (ULONG *) & gad)))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL, GTCY_Active, mode, TAG_DONE);
    }
}

 /* sample function returning an error */
VOID ErrorFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    STRPTR name = "Uh Oh!";

    /* sample error return */
    ai->ai_Pri_Ret = RETURN_ERROR;
    ai->ai_Sec_Ret = ERROR_DISPLAY;
    ai->ai_TextRtn = PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, (int) name);
}

/* Given a screen pointer, determine the public screen name */
VOID PubScreenName (struct Screen * scr, STRPTR buffer)
{
    struct Screen *cs = NULL;
    struct List *publist;
    struct List copy_publist;
    struct PubScreenNode *psnode;
    struct PubScreenNode *copy_psnode;

    /* Initialize our variables */
    strcpy (buffer, "<private>");
    NewList (&copy_publist);

    /* Lock the public screen list */
    publist = LockPubScreenList ();

    /* and copy it */
    for (psnode = (struct PubScreenNode *) publist->lh_Head;
	 psnode->psn_Node.ln_Succ;
	 psnode = (struct PubScreenNode *) psnode->psn_Node.ln_Succ)
    {
	if (copy_psnode = AllocMem (sizeof (struct PubScreenNode), MEMF_CLEAR))
	{
	    /* Copy the structure */
	    *copy_psnode = *psnode;

	    /*
	     * ln_Name points to the public screen name, make your own copy
	     */
	    if (copy_psnode->psn_Node.ln_Name = AllocMem (strlen (psnode->psn_Node.ln_Name) + 1, MEMF_CLEAR))
	    {
		strcpy (copy_psnode->psn_Node.ln_Name, psnode->psn_Node.ln_Name);
	    }

	    AddTail (&copy_publist, (struct Node *) copy_psnode);
	}
    }

    UnlockPubScreenList ();

    psnode = (struct PubScreenNode *) copy_publist.lh_Head;
    while ((copy_psnode = (struct PubScreenNode *) psnode->psn_Node.ln_Succ) &&
	   (cs == NULL))
    {
	if (psnode->psn_Screen == scr)
	{
	    strcpy (buffer, psnode->psn_Node.ln_Name);
	    cs = psnode->psn_Screen;
	}

	if (psnode->psn_Node.ln_Name)
	{
	    FreeMem (psnode->psn_Node.ln_Name, strlen (psnode->psn_Node.ln_Name) + 1);
	}
	Remove ((struct Node *) psnode);
	FreeMem (psnode, sizeof (struct PubScreenNode));
	psnode = copy_psnode;
    }
}

/* sample function returning ARexx stem variables */
VOID GetInfoFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    /* ARexx function prototypes */
    extern LONG SetRexxVar (struct RexxMsg *, UBYTE *, UBYTE *, LONG);
    extern LONG CheckRexxMsg (struct RexxMsg *);

    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    UBYTE stem[128], value[140];
    struct MsgHandler *mh;
    struct RexxMsg *msg;
    struct Funcs *f;
    LONG kind;

    /* Build error string */
    ai->ai_Pri_Ret = RETURN_WARN;
    ai->ai_Sec_Ret = ERROR_CANT_RVI;
    ai->ai_TextRtn = PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, NULL);

    /* Find out what type the current message is */
    if (GetAPSHAttr (ai, APSH_ActvMH, ai, &kind))
    {
	/* See if the current message is an ARexx message */
	if (kind == APSH_AREXX_ID)
	{
	    /* Get a pointer to the current message */
	    if (GetAPSHAttr (ai, APSH_ActvMessage, ai, &msg))
	    {
		/* Check to see if we can set an RVI on this message. */
		if (CheckRexxMsg (msg))
		{
		    /* Get the destination stem name */
		    sprintf (stem, "%s.", ai->ai_BaseName);
		    if (f = af->af_FE)
		    {
			if (f->fe_Options[0])
			{
			    strcpy (stem, (STRPTR) f->fe_Options[0]);
			}
		    }

		/* Send the application version */

		    /* Build the version variable */
		    sprintf (ad->ad_TmpText, "%sVERSION", stem);
		    strcpy (value, ai->ai_AppVersion);

		    /* Set the RVI's */
		    SetRexxVar (msg, ad->ad_TmpText, value, strlen (value));

		/* Send the current public screen name */

		    /* Build the screen name variable */
		    value[0] = 0;
		    sprintf (ad->ad_TmpText, "%sSCREEN", stem);
		    if (ai->ai_ScreenName)
		    {
			strcpy (value, ai->ai_ScreenName);
		    }
		    else
		    {
			PubScreenName (ai->ai_Screen, value);
		    }

		    /* Set the RVI's */
		    SetRexxVar (msg, ad->ad_TmpText, value, strlen (value));

		/* Send the ARexx port name */

		    /* Build the ARexx port name variable */
		    sprintf (ad->ad_TmpText, "%sAREXX", stem);

		    /* Build the value string */
		    strcpy (value, "<unnamed>");
		    if (GetAPSHAttr (ai, APSH_ARexxMH, ai, &mh) && mh)
		    {
			strcpy (value, mh->mh_PortName);
		    }

		    /* Set the RVI's */
		    SetRexxVar (msg, ad->ad_TmpText, value, strlen (value));

		/* Send the current mode */

		    /* Build the variable name */
		    sprintf (ad->ad_TmpText, "%sMODE", stem);

		    /* Build the value string */
		    strcpy (value, "<unset>");
		    if (ad->ad_Mode == 0)
			strcpy (value, "Male");
		    else if (ad->ad_Mode == 1)
			strcpy (value, "Female");
		    else if (ad->ad_Mode == 2)
			strcpy (value, "Zombie");

		    /* Set the RVI's */
		    SetRexxVar (msg, ad->ad_TmpText, value, strlen (value));

		/* Return the name of the destination stem variable */

		    /* Let them know what the stem variable is named */
		    strcpy (ad->ad_TmpText, stem);
		    ai->ai_Pri_Ret = RETURN_OK;
		    ai->ai_Sec_Ret = NULL;
		    ai->ai_TextRtn = ad->ad_TmpText;
		}
	    }
	}
    }
}

 /* Shutdown routine */
VOID QuitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    /* Tell the AppShell that we're all done now. */
    ai->ai_Done = TRUE;
}
