/*
 * ham8.c Michael Saunby   M.Saunby@reading.ac.uk
 *
 * Release 1.1   December 1992 A public screen plus utilities to give a pseudo
 * 24 bit display on Amigas with AGA graphics (A1200 and A4000).
 *
 * The AGA HAM-8 mode allows 64 colours to be defined - 48 of these will be
 * defined by this program (see display24.c) leaving 8 for intuition and
 * client programs.
 */
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <graphics/displayinfo.h>
#include <intuition/screens.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <exec/memory.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/gadtools.h>

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

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

#include "display24.h"

/*
 * If you turn this into something different feel free to change this, but if
 * you don't leave it alone!!
 */
#define COPYRIGHT_MESSAGE "Version 1.1\nDecember 1992\n\nCopyright © 1992\n\
Michael Saunby"

/*
 * Template etc for ReadArgs() - Pity it can't read tooltypes
 */
#define TEMPLATE "W=Width/N,H=Height/N,L=LoRes/S"
#define OPT_WIDTH 0
#define OPT_HEIGHT 1
#define OPT_LORES 2
#define OPT_COUNT 3

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

extern struct ExecBase *SysBase;

BOOL restart = FALSE;		/* used for resize - see main() */
UWORD width, height;
BOOL lores;
struct Screen *screen;
struct MsgPort *messageport;
ULONG allocsignal;

/*
 * Message structure used to get screen size changed, see main()
 */
struct TestMsg
{
  struct Message msg;
  USHORT Width;
  USHORT Height;
  BOOL Lores;
};

/*
 * Easy Requester used for error messages
 */
struct EasyStruct failedES =
{sizeof (struct EasyStruct), 0, HAM8_ALBERT_NAME,
 "%s", "OK",};

/*
 * Gadtools menu
 */
#define ABOUT 1
#define QUIT 2
struct NewMenu prj_menu[] =
{
  {NM_TITLE, "Project", 0, 0, 0, 0,},
  {NM_ITEM, "About...", "?", 0, 0, (void *) ABOUT,},
  {NM_ITEM, "Quit", "Q", 0, 0, (void *) QUIT,},
  {NM_END, NULL, 0, 0, 0, 0,},
};


/*
 * Cleanup() - Close screen and libraries before exit
 */
VOID Cleanup (VOID);

VOID
Cleanup ()
{
  if (restart)
    {
      /*
       * Failed to open screen for client. There is a message port waiting
       * for a reply.
       */
    }
  if (screen)
    {
      CloseScreen (screen);
      FreeSignal (allocsignal);
    }
  if (GadToolBase)
    CloseLibrary (GadToolBase);
  if (IntuitionBase)
    CloseLibrary ((struct Library *) IntuitionBase);
  if (GfxBase)
    CloseLibrary ((struct Library *) GfxBase);

}

/*
 * Quit() - Cleanup and exit on error
 */
VOID Quit ();

VOID
Quit (char whytext[], UBYTE failcode)
{
  EasyRequest (NULL, &failedES, NULL, whytext);
  Cleanup ();
  DeletePort (messageport);
  exit (failcode);
}

/*
 * CreateScreen() - Main function, build and open a public screen
 */
VOID
CreateScreen ( /* USES GLOBALS width, height, lores, restart */ )
{
  ULONG modeID = HIRESHAMLACE_KEY;
  DisplayInfoHandle displayhandle;
  struct DimensionInfo dimensioninfo;

  UWORD maxdepth;
  ULONG soerror = NULL;
  ULONG signal, wsignal, lastsignal;
  ULONG psignal;		/* Message port - Another copy of program
				 * started */
  struct Task *task;
  BOOL abort = FALSE, closedown = FALSE;
  UWORD oldstatus;
  struct IntuiMessage *msg;
  static struct TestMsg *newdims_msg;	/* MUST be static, reply is delayed! */
  struct Window *window;
  struct Menu *menu;
  void *vi;


  if (lores)
    modeID = HAM_KEY;

  if ((GfxBase =
       (struct GfxBase *) OpenLibrary ("graphics.library", 36)) == NULL)
    Quit ("graphics.library is too old <V36", 25);

  if ((IntuitionBase =
    (struct IntuitionBase *) OpenLibrary ("intuition.library", 36)) == NULL)
    Quit ("intuition.library is too old <V36", 25);

  if ((displayhandle = FindDisplayInfo (modeID)) == NULL)
    Quit ("modeID not found in display database", 25);

  if (GetDisplayInfoData (displayhandle, (UBYTE *) & dimensioninfo,
		       sizeof (struct DimensionInfo), DTAG_DIMS, NULL) == 0)
    Quit ("mode dimension info not available", 25);

  /*
   * Public screen needs a signal and pointer to this task.
   */
  if ((allocsignal = AllocSignal (-1)) == -1)
    Quit ("could not allocate signal", 25);

  lastsignal = 1 << allocsignal;

  task = (struct Task *) & ((struct Process *) FindTask (NULL))->pr_Task;

  /*
   * MaxDepth will always be 8 for Ham8 mode.  But it can't hurt.
   */
  maxdepth = dimensioninfo.MaxDepth;
  /*
   * If you always want a standard size just use SA_Overscan, OSCAN_TEXT
   * etc.  I have to get the dimensions from dimensioninfo because I always
   * set the width and height.
   */
  if (width == 0)
    width = dimensioninfo.TxtOScan.MaxX - dimensioninfo.TxtOScan.MinX;
  if (height == 0)
    height = dimensioninfo.TxtOScan.MaxY - dimensioninfo.TxtOScan.MinY;

  /*
   * Screen is based on workbech to give a familiar look. Could possibly
   * cause problems is the default is a 256 colour WB, must try that
   * sometime and see what happens.
   */
  if ((screen = OpenScreenTags (NULL,
				SA_LikeWorkbench, TRUE,	/* see above */
				SA_Overscan, OSCAN_TEXT,
				SA_AutoScroll, TRUE,
				SA_DisplayID, modeID,
				SA_Depth, (UBYTE) maxdepth,
				SA_Width, width,
				SA_Height, height,
				SA_Title, HAM8_ALBERT_NAME,
				SA_PubName, HAM8_ALBERT_NAME,
				SA_PubSig, allocsignal,
				SA_PubTask, task,
				SA_ErrorCode, &soerror,
				SA_SharePens, TRUE,
				TAG_END)) == NULL)
    {
      /*
       * There are many reasons why this might fail, though on an AGA
       * machine only NOCHIPMEM is likely.  The other tests are worth
       * keeping for when this code is next used.
       */
      switch (soerror)
	{
	case OSERR_NOCHIPS:
	  Quit ("Amiga custom chips not AGA", 25);
	  break;

	case OSERR_UNKNOWNMODE:
	  Quit ("Cannot open screen in this mode", 25);
	  break;

	case OSERR_NOCHIPMEM:
	  Quit ("Not enough CHIP memory", 25);
	  break;

	case OSERR_NOMEM:
	  Quit ("Not enough memory (CHIP or FAST)", 25);
	  break;

	default:
	  Quit ("Unspecified error opening screen", 25);
	  break;
	}
    }
  /*
   * StandardPalette() returns number of errors (see display24.c) I ignore
   * the errors here and leave it to the client programs to deal with.
   */
  (VOID) StandardPalette (&(screen->ViewPort), FALSE);

  if ((menu = CreateMenus (prj_menu, GTMN_FrontPen, 1, TAG_DONE)) == NULL)
    Quit ("could not build menu", 25);

  /*
   * Open a backdrop window to which the above menu is attached.
   */
  if ((window = OpenWindowTags (NULL,
				WA_Top, screen->BarHeight + 1,
				WA_Height, screen->Height -
				(screen->BarHeight + 1),
				WA_CustomScreen, screen,
				WA_NewLookMenus, TRUE,
				WA_IDCMP, MENUPICK,
		 WA_Flags, ACTIVATE | BACKDROP | BORDERLESS | NOCAREREFRESH,
				TAG_END)) == NULL)
    Quit ("could not open backdrop window", 25);

  /*
   * visual info need to get proper look for menu
   */
  vi = GetVisualInfo (screen, TAG_END);
  if (LayoutMenus (menu, vi, TAG_DONE) == NULL)
    Quit ("could not layout menu", 25);
  FreeVisualInfo (vi);
  SetMenuStrip (window, menu);

  /*
   * now go public
   */
  oldstatus = PubScreenStatus (screen, 0);


  /*
   * come forward if and window opens on this screen
   */
  SetPubScreenModes (POPPUBSCREEN);

  if (restart)
    {
      ReplyMsg ((struct Message *) newdims_msg);
      restart = FALSE;
    }
  /*
   * Now wait until told to quit by user (from menu) waiting of course for
   * any visitors to leave.
   */
  psignal = 1 << messageport->mp_SigBit;
  wsignal = 1 << window->UserPort->mp_SigBit;

  do
    {
      signal = Wait (wsignal | lastsignal | psignal);
      if (signal & psignal)
	{
	  newdims_msg = (struct TestMsg *) GetMsg (messageport);
	  width = newdims_msg->Width;
	  height = newdims_msg->Height;
	  lores = newdims_msg->Lores;
	  restart = TRUE;
	  /* NB. no reply to message until we have new screen */
	  if ((oldstatus = PubScreenStatus (screen,
					    PSNF_PRIVATE)) & 1)
	    {
	      abort = TRUE;
	    }
	  else
	    {
	      closedown = TRUE;
	      EasyRequest (window, &failedES,
			   NULL,
			   "Cannot resize yet, there are other programs");
	    }
	}
      if (signal & lastsignal)
	{

	  if (closedown)
	    if ((oldstatus = PubScreenStatus (screen, PSNF_PRIVATE))
		& 1)
	      abort = TRUE;
	}
      while (msg = (struct IntuiMessage *) GetMsg (window->UserPort))
	{

	  if (msg->Class == MENUPICK)
	    {
	      switch ((UWORD) MENU_USERDATA (ItemAddress (menu, msg->Code)))
		{
		case ABOUT:
		  EasyRequest (window, &failedES,
			       NULL,
			       COPYRIGHT_MESSAGE);
		  break;
		case QUIT:
		  if ((oldstatus = PubScreenStatus (screen,
						    PSNF_PRIVATE)) & 1)
		    {
		      abort = TRUE;
		    }
		  else
		    {
		      closedown = TRUE;
		      EasyRequest (window, &failedES,
				   NULL,
			      "Cannot Quit yet,\nthere are other programs");
		    }
		  break;
		}
	    }
	  ReplyMsg ((struct Message *) msg);
	}

  } while (abort == FALSE);

  /*
   * Cleanup part 1. (part 2 is Cleanup())
   */
  ClearMenuStrip (window);

  CloseWindow (window);

  Cleanup ();

}

/*
 * If another copy of this program is already running we just tell it what
 * size of screen we want then exit.
 */

void
main ()
{
  LONG result[OPT_COUNT] =
  {0, 0, 0};
  struct RDArgs *rda;
  BOOL running = FALSE;		/* set to true if we find another
				 * copy running */
  struct MsgPort *testport, *progport;
  struct TestMsg *msg;

  /*
   * Default values - 0 means Text overscan size ie Workbench like defaults
   */
  width = 0;
  height = 0;
  lores = FALSE;

  rda = ReadArgs (TEMPLATE, result, NULL);

  if (result[OPT_WIDTH])
    width = *(LONG *) result[OPT_WIDTH];
  if (result[OPT_HEIGHT])
    height = *(LONG *) result[OPT_HEIGHT];
  if (result[OPT_LORES])
    lores = TRUE;
  FreeArgs (rda);

  /*
   * Check if copy of program running already
   */
  if (testport = CreatePort (NULL, NULL))
    {
      if (msg = AllocMem (sizeof (struct TestMsg), MEMF_PUBLIC | MEMF_CLEAR))
	{
	  msg->msg.mn_ReplyPort = testport;
	  msg->msg.mn_Length = sizeof (struct TestMsg);
	  msg->Width = width;
	  msg->Height = height;
	  msg->Lores = lores;
	  Forbid ();
	  if (progport = FindPort (HAM8_ALBERT_NAME))
	    {
	      PutMsg (progport, (struct Message *) msg);
	      Permit ();
	      WaitPort (testport);
	      msg = (struct TestMsg *) GetMsg (testport);
	      running = TRUE;
	    }
	  Permit ();
	  FreeMem (msg, (ULONG) sizeof (struct TestMsg));
	}
      DeletePort (testport);
    }
  if (running)
    return;

  messageport = CreatePort (HAM8_ALBERT_NAME, NULL);

  /*
   * Go around this loop each time the screen is resized. al-backdrop.c
   * uses this feature.
   */
  do
    {
      CreateScreen (lores, width, height);
  } while (restart);
  DeletePort (messageport);
}
