/* main.c -- new public screen demo :ts=4 */
/*
Copyright (c) 1989 Commodore-Amiga, Inc.

Executables based on this information may be used in software
for Commodore Amiga computers. All other rights reserved.
This information is provided "as is"; no warranties are made.
All use is at your own risk, and no liability or responsibility
is assumed.
*/

#include "sysall.h"
#include "tmsg.h"
#include "pubsc.h"

#define D(x)	;
#define DPS(x)	;

struct Gadget	*commandGadgets();
struct Gadget 	*buttonGadgets();
struct  Window	*getVisitor();
struct TextMsg	*CreateTextMsg();

struct Library	*IntuitionBase = NULL;
struct Library	*GfxBase = NULL;

#define ASIZE(a)	(sizeof( a )/sizeof ( a[0] ))
#define IDMASK	(0x00ff)	/* mask with this to get id in category	*/

UBYTE	*commstr[] = {
	(UBYTE *) "Close Screen",
	(UBYTE *) "Make Default",
	(UBYTE *) "Open New",
	(UBYTE *) "Jump",
};

#define COMMAND_ID	(0x0200)

/*** button state gadget bookkeeping	***/
UBYTE	*buttonstr[] = {
	(UBYTE *) "Auto Pop-to-front",
	(UBYTE *) "Hijack Workbench",
};

#define BUTTON_ID	(0x0100)
struct Gadget	*buttons[ ASIZE( buttonstr ) ];

/*
 * The ways these work:
 *	Currname is the name of the screen we're open on, or would
 *	like to open on.  
 *	It's the null string if we're open on the Workbench.
 */
UBYTE			currname[ MAXPUBSCREENNAME] = "";
struct Screen	*lockedscreen = NULL;
struct Window   *window = NULL;

struct Gadget	*usergadgets = NULL;

struct TextMsg	*currmsg = NULL;
struct TextMsg	*defmsg;
struct TextMsg	*statusmsg;

WORD			saveWLeft = 120;
WORD			saveWTop = 80;

struct RastPort	*windowrp;	/* my window's RastPort			*/
UBYTE			defname[ MAXPUBSCREENNAME ] = "";

main()
{
    struct  IntuiMessage	*msg;
	struct Screen	wb;	/* get copy from getscreendata		*/
	struct Screen	*screen;
	UBYTE			toUpper();


    /* hold data from *msg  */
    ULONG   		class;
    USHORT  		code;
    struct Gadget    	*iaddr;
    UWORD		qualifier;
	int			gid;


    if (! (IntuitionBase =  OpenLibrary("intuition.library", 36L) ) )
		cleanup( "no v36 Intuition", 20 );

    if ( ! (GfxBase =   OpenLibrary("graphics.library", 36L) ) )
		cleanup( "no v36 Graphics", 20 );


	/* Open up on default public screen	*/
	lockPubScreen();
	upWindow();

    FOREVER
    {
		if ((msg = (struct IntuiMessage *)GetMsg(window->UserPort)) == NULL)
		{
		    Wait(1L<<window->UserPort->mp_SigBit);
		    continue;
		}

		/* Stash message contents and reply,
		 * important when message triggers some
		 * lengthy processing
		 */

		class   = msg->Class;
		code    = msg->Code;
		iaddr   = (struct Gadget *) msg->IAddress;
		qualifier = msg->Qualifier;

		ReplyMsg(msg);

		switch (class)
		{
		case REFRESHWINDOW:
			BeginRefresh( window );

			RefreshTextMsg( windowrp, currmsg, -1 );

			EndRefresh( window, 1L );
			break;

		case VANILLAKEY:
			/* look for key in command gadget list	*/
			/* ZZZZ: do case conversion	*/
			if ( code == 'q' || code == 'Q' ) cleanup( "quit.", 0 );

			code = toUpper( code );

			for ( gid = 0; gid < c_number; ++gid )
			{
				if ( toUpper( *(commstr[ gid ]) ) == code )
				{
					doCommand( gid );
					break;
				}
			}
		    break;

		case GADGETDOWN:
			setPSModes();

		    break;

		case GADGETUP:
			gid = iaddr->GadgetID;
			if ( ( gid & ~IDMASK ) == COMMAND_ID )
			{
				doCommand( gid & IDMASK );
			}
		    break;

		case CLOSEWINDOW:
		    cleanup( "all done", 0 );
		}
    }
}

cleanup( str, code )
UBYTE	*str;
{

	downWindow();

    if ( str ) printf( "%s\n", str );

    if (GfxBase) CloseLibrary(GfxBase);
    if (IntuitionBase) CloseLibrary(IntuitionBase);

    exit ( code );
}

UBYTE toUpper(u)
UBYTE u;
{

    if ((u >= 0x61 && u <= 0x7a) ||
        (u >= 0xe0 && u <= 0xf6) || 
        (u >= 0xf8 && u <= 0xfe)) u &= 0xdf;

    return(u);
}

psstatus( str )
char	*str;
{
	D( printf("psstatus: %s\n", str ) );
    ChangeTextMsg( windowrp, statusmsg, str );
}

/*
 * fetches screen names and updates display
 */
updateMessages()
{
	GetDefaultPubScreen( defname );

	ChangeTextMsg( windowrp, currmsg, "Current screen: %s", 
		currname[0]? currname: (UBYTE *) defname );
	ChangeTextMsg( windowrp, defmsg, "Default screen: %s", defname );
	ChangeTextMsg( windowrp, statusmsg, "" );
}

/*
 * read gadgets and tell Intuition
 */
setPSModes()
{
	struct Gadget	*g;
	UWORD			modes = 0;

	if ( buttons[ b_autopop ]->Flags & SELECTED )  modes |= POPPUBSCREEN;
	if ( buttons[ b_hijack ]->Flags & SELECTED )  modes |= SHANGHAI;

	SetPubScreenModes( (long) modes );
}

/*
 * sync my gadgets up with actual modes.
 * no refresh.  returns non-zero if my gadgets were wrong.
 */
getPSModes()
{
	UWORD	modes;
	UWORD	mymodes;
	int		changed;

	/* get modes	*/
	Forbid();
	modes = SetPubScreenModes( 0L );
	SetPubScreenModes( (long) modes );
	Permit();

	/* get my modes, to detect change	*/
	if ( buttons[ b_autopop ]->Flags & SELECTED )  mymodes |= POPPUBSCREEN;
	if ( buttons[ b_hijack ]->Flags & SELECTED )  mymodes |= SHANGHAI;

	if ( modes & SHANGHAI )	buttons[ b_hijack ]->Flags |= SELECTED;
	if ( modes & POPPUBSCREEN )	buttons[ b_autopop ]->Flags |= SELECTED;

	return ( mymodes ^ modes );
}


/* ---------------------------------------------------------------	*/
/* 		Layout and initialization of application window				*/
/* ---------------------------------------------------------------	*/

upWindow()
{
	DPS( printf("upWindow, currname: %s\n", currname ) );
	if ( ! lockedscreen ) cleanup( "upWindow: no lock on screen\n" );

	DPS( printf("upWindow locked screen: %lx\n", lockedscreen ) );

	initWindow( lockedscreen, saveWLeft, saveWTop ); /* will cleanup() */
	windowrp = window->RPort;

	UnlockPubScreen( NULL, lockedscreen );
	lockedscreen = NULL;

	updateMessages();
}

downWindow()
{
	DPS( printf("down window.  currname %s\n", currname) );

    if (window)
	{
		saveWLeft = window->LeftEdge;
		saveWTop = window->TopEdge;		/* scale these back	*/

		DeleteTextMsg( currmsg, -1 );
		currmsg = NULL;

		if ( usergadgets )
		{
		    RemoveGList( window, usergadgets, -1L );
			FreeGadgets( usergadgets );
			usergadgets = 0;
			cleanupCommGadgets();
			cleanupButtonGadgets();
		}
		CloseWindow(window);
		window = NULL;
	}
}

struct LayoutBox {
	int	Left;
	int	Top;
	int	Width;
	int	Height;
};

/* These quantities should scale by resolution
 * or otherwise determined by more dynamic means
 */
#define LAYOUT_LEFT	(20)
#define LAYOUT_TOP	(5)			/* add this to title bar height		*/
#define RIGHT_MARGIN  (20)		/* size gadget width, plus slop		*/
#define BOTTOM_MARGIN (5)
#define VFILL		(2)			/* vertical space between items		*/
#define HFILL		(28)			/* horizontal space between columns	*/
/* Also, need to communicate gadgetborder thicknesses to
 * gadget building routines
 */

ULONG   flg = ACTIVATE|WINDOWCLOSE|WINDOWDRAG|
				WINDOWDEPTH|SIMPLE_REFRESH | VISITOR;
ULONG   iflg = GADGETDOWN|GADGETUP|VANILLAKEY|CLOSEWINDOW|REFRESHWINDOW;

initWindow( sc, wleft, wtop )
struct Screen	*sc;
{
	struct RastPort	*rp;	/* used only for text size calculations	*/
	struct Gadget	*g = (struct Gadget *) &usergadgets; /* tail of list */

	/* layout	*/
	struct LayoutBox	lbox;
	int		gleft = LAYOUT_LEFT;	/* running position	*/
	int		gtop;
	WORD	tmheight;

	int		wwidth;
	int		wheight;

	rp = &sc->RastPort;

	/* start placement within window borders	*/
	gleft = sc->WBorLeft + LAYOUT_LEFT;
	gtop = LAYOUT_TOP + sc->WBorTop + sc->RastPort.TxHeight + 1;

	/* initialize layout	*/
	initLayoutBox( &lbox );

    g->NextGadget = commandGadgets(rp, commstr, ASIZE(commstr), COMMAND_ID);
	if ( ! g->NextGadget )	cleanup( "commandGadget failed.", 20 );

	/* column placement.  note trick of walking to end of list	*/
	while ( g->NextGadget )
	{
		g = g->NextGadget;

		g->LeftEdge = gleft;
		g->TopEdge = gtop;
		gtop += (g->Height + VFILL);

		gadgetExpand( &lbox, g );		/* track layout	*/
	}

	/* next column	*/
	gtop = lbox.Top;
	gleft = lbox.Left + lbox.Width + HFILL;

	/* start column with two screen status messages	*/
	if (!(currmsg = CreateTextMsg( 128 ) ) ) cleanup( "no TextMessage!", 20 );
	if (!(defmsg = CreateTextMsg( 128 ) ) ) cleanup( "no TextMessage!", 20 );
	if (!(statusmsg = CreateTextMsg( 128 ) ) )
		cleanup( "no TextMessage!", 20 );

	currmsg->tm_Next = defmsg;
	defmsg->tm_Next = statusmsg;

	/* position current screen message */
	PositionTextMsg( currmsg, gleft, gtop );
	TextMsgSize( rp, currmsg, NULL, &tmheight );
	expandLayout( &lbox, gleft, gtop, 0, tmheight );
	gtop += tmheight + VFILL;

	/* position default screen message */
	PositionTextMsg( defmsg, gleft, gtop );
	TextMsgSize( rp, defmsg, NULL, &tmheight );
	expandLayout( &lbox, gleft, gtop, 0, tmheight );
	gtop += tmheight + VFILL;

	/* position status message later	*/

    g->NextGadget = buttonGadgets(rp, buttonstr, ASIZE(buttonstr), BUTTON_ID);
    if ( ! g->NextGadget ) cleanup( "buttonGadgets failed.", 20 );

	/* column placement.  note trick of walking to end of list	*/
	while ( g->NextGadget )
	{
		g = g->NextGadget; 
		/* position */
		g->LeftEdge = gleft;
		g->TopEdge = gtop;
		gtop += (g->Height + VFILL);

		gadgetExpand( &lbox, g );		/* track layout	*/

		/* stash in array	*/
		buttons[ g->GadgetID & IDMASK ] = g;
	}

	getPSModes();	/* init my SELECTED flags	*/

	/* layout, limit text message, to be aligned with others	*/
	PositionTextMsg( statusmsg, lbox.Left, lbox.Top + lbox.Height + VFILL );
	TextMsgSize( rp, statusmsg, NULL, &tmheight );
	expandLayout( &lbox, lbox.Left,
		lbox.Top + lbox.Height + VFILL , 0, tmheight );

	D( printf("layout l/t/w/h: %d/%d/%d/%d\n", lbox.Left, lbox.Top,
		lbox.Width, lbox.Height ) );

	/* determine desired window dimensions				*/
	wwidth = lbox.Left + lbox.Width + RIGHT_MARGIN;
	wheight = lbox.Top + lbox.Height + BOTTOM_MARGIN;

	D(printf("naive dims %d/%d/%d/%d\n", wleft, wtop, wwidth, wheight ) );

	/* adjust left top to make more room, if necessary	*/
	wleft = max( 0, min( wleft, sc->Width - wwidth ) );
	wtop = max( 0, min( wtop, sc->Height - wheight ) );

	D( printf("shifted dims %d/%d/%d/%d\n", wleft, wtop, wwidth, wheight ) );
	D( printf("screen w/h %d/%d\n", sc->Width, sc->Height ) );

	/* double check for legal width, height				*/
	wwidth = min( wwidth, sc->Width - wleft );
	wheight = min( wheight, sc->Height - wtop );

	/* just keep the messages in window	*/
	LimitTextMsg( statusmsg, wwidth - sc->WBorRight -  lbox.Left );
	LimitTextMsg( currmsg, wwidth - sc->WBorRight -  gleft );
	LimitTextMsg( currmsg, wwidth - sc->WBorRight -  gleft );

	D( printf("window %d/%d/%d/%d\n", wleft, wtop, wwidth, wheight ) );

    window = getVisitor( wleft, wtop, wwidth, wheight, sc, flg, iflg);
    if ( ! window ) cleanup( "can't get window", 20 );

	/* show me	*/
    AddGList( window, usergadgets, -1L, -1L );
    RefreshGList( usergadgets, window, NULL, -1L );
}

/* ---------------------------------------------------------------	*/
/* 						Layout utilities							*/
/* ---------------------------------------------------------------	*/

initLayoutBox( lb )
struct LayoutBox	*lb;
{
	lb->Left = lb->Top  = 32000;
	lb->Width = lb->Height = -32000;
}

expandLayout( lb, l, t, w, h )
struct LayoutBox	*lb;
{
	int	margin;

	if ( (margin = lb->Left - l) > 0 )
	{
		lb->Left = l;
		lb->Width += margin;
	}

	if ( (margin = lb->Top - t) > 0 )
	{
		lb->Top = t;
		lb->Height += margin;
	}

	forceHigh( &lb->Width, l + w - lb->Left );
	forceHigh( &lb->Height, t + h - lb->Top );
}

forceHigh( valp, low )
WORD	*valp;
WORD	low;
{ if ( *valp < low ) *valp = low; }

gadgetExpand( lb, g )
struct LayoutBox	*lb;
struct Gadget		*g;
{ expandLayout( lb, g->LeftEdge, g->TopEdge, g->Width, g->Height ); }

struct  Window * getVisitor(left, top, width, height, screen, flg, iflg)
SHORT   left, top, width, height;
struct Screen	*screen;
ULONG   flg, iflg;
{
    struct  Window  *OpenWindow();
    struct  NewWindow   nw;

	DPS( printf("getV: screen %lx\n", screen ) );

    nw.LeftEdge =   (SHORT) left;
    nw.TopEdge  =   (SHORT) top;
    nw.Width    =   (SHORT) width;
    nw.Height   =   (SHORT) height;
    nw.DetailPen    =   (UBYTE) -1;
    nw.BlockPen =   (UBYTE) -1;
    nw.IDCMPFlags   =   (ULONG) iflg;

    nw.Flags    =   (ULONG) flg;

    nw.FirstGadget  =   (struct Gadget *)   NULL;
    nw.CheckMark    =   (struct Image *)    NULL;
    nw.Title    =   (UBYTE *)   " Public Screen Demo ";
    nw.Screen   =   screen;
    nw.BitMap   =   (struct BitMap *)   NULL;
    nw.MinWidth =   (SHORT) 50;
    nw.MinHeight=   (SHORT) 30;
    /* work around bug  */
    nw.MaxWidth =   (SHORT) nw.Width;
    nw.MaxHeight    =   (SHORT) nw.Height;
    nw.Type     =   (USHORT) (screen? CUSTOMSCREEN: WBENCHSCREEN);
	DPS( printf("gV type: %x\n", nw.Type ) );

    return ((struct Window *) OpenWindow(&nw));
}
