/* main.c -- new public screen demo :ts=8 */
/*
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 DL(x)	;
#define DPS(x)	;

struct Gadget	*commandGadgets();
struct Gadget 	*buttonGadgets();
struct  Window	*getVisitor();
struct TextMsg	*CreateTextMsg();
void forceHigh(WORD	*valp,WORD	low);
void initLayoutBox(struct LayoutBox	*lb );
void gadgetExpand(struct LayoutBox	*lb,struct Gadget		*g);
void expandLayout(struct LayoutBox	*lb, int l, int t, int w, int h );
void cleanup(UBYTE	*str, int code );
void downWindow(void);
void upWindow(void);
void setPSModes(void);
void initWindow(struct Screen	*sc, int wleft, int wtop );

struct Library	*IntuitionBase = NULL;
struct Library	*GfxBase = NULL;
struct Library	*OpenLibrary();

#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 ] = "";

void main(void)
{
    struct  IntuiMessage	*msg;
    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((struct Message *)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 );
	}
    }
}

void cleanup(UBYTE	*str, int code )
{

    downWindow();

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

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

    exit ( code );
}

UBYTE toUpper(UBYTE u)
{

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

    return(u);
}

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

/*
 * fetches screen names and updates display
 */
void updateMessages(void)
{
    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
 */
void setPSModes(void)
{
    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.
 */
UWORD getPSModes(void)
{
    UWORD	modes;
    UWORD	mymodes = 0;

    /* 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			*/
/* ---------------------------------------------------------------	*/

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

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

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

    UnlockPubScreen( NULL, lockedscreen );
    lockedscreen = NULL;

    updateMessages();
}

void downWindow(void)
{
    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 {
    WORD	Left;
    WORD	Top;
    WORD	Width;
    WORD	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	(60)	/* right side 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
 */


void initWindow(struct Screen	*sc, int wleft, int wtop )
{
    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;
    DL( printf("rastport text height: %d\n", rp->TxHeight ));

    /* start placement within window borders	*/
    gleft = sc->WBorLeft + LAYOUT_LEFT;
    DL( printf("left before commgads: %d\n", gleft ));
    gtop = LAYOUT_TOP + sc->WBorTop + rp->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;
    DL( printf("left after commgads: %d\n", gleft ));

    /* 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 = jmax( 0, jmin( wleft, sc->Width - wwidth ) );
    wtop = jmax( 0, jmin( 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 = jmin( wwidth, sc->Width - wleft );
    wheight = jmin( wheight, sc->Height - wtop );

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

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

    window = getVisitor( wleft, wtop, wwidth, wheight, sc );
    DPS( printf("visitor open at %lx\n", window ));
    if ( ! window ) cleanup( "can't get window", 20 );

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

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

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

void expandLayout(struct LayoutBox	*lb, int l, int t, int w, int h )
{
    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 );
}

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

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

struct  Window * getVisitor(WORD left, WORD top, WORD width, WORD height,struct Screen	*screen)
/* locked public screen, or NULL */
{
    struct  Window  *OpenWindowTags();

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

    return ( OpenWindowTags( NULL,
    	WA_Left,	left,
    	WA_Top,		top,
    	WA_Width,	width,
    	WA_Height,	height,
    	WA_IDCMP,  GADGETDOWN|GADGETUP|VANILLAKEY|CLOSEWINDOW|REFRESHWINDOW,
	WA_Activate,	TRUE,
	WA_CloseGadget,	TRUE,
	WA_DepthGadget,	TRUE,
	WA_DragBar,	TRUE,
	WA_SimpleRefresh, TRUE,
    	WA_Title,	 " Public Screen Demo ",
    	WA_PubScreen,	screen,
	TAG_END ) );
}

/* convert varargs to array	*/
/*
struct Window	*OpenWindowTags(struct NewWindow	*nw,ULONG tag1)
{
    struct  Window  *OpenWindowTagList();

    return ( OpenWindowTagList( nw, &tag1 ) );
}
*/
