/*                                                               -*- C -*-
**  Amiga Graphics for ASTROLOG
**
**  (c)Copyright 1994-95 by Tobias Ferber,  All Rights Reserved
*/

/* $VER: $Id: amiga.c,v 1.8 1995/08/11 12:13:49 tf Exp $ */

#include <exec/types.h>
#include <exec/memory.h>

#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>

#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/view.h>

#include <stdio.h>

#include "astrolog.h"
#include "rastport.h"

/*#define NOSCREEN*/
    /* compile with -DNOSCREEN in order to get a window which opens on the Workbench screen
     * and uses ObtainBestPen() to get it's colors.  (VERY UNSTABLE!) */

/* Prototypes */

extern ULONG AvailMem(ULONG);
extern struct Screen *OpenScreenTagList( struct NewScreen *, struct TagItem * );
extern struct Screen *LockPubScreen( UBYTE * );
extern VOID UnlockPubScreen( UBYTE *, struct Screen * );
extern VOID DrawImage( struct RastPort *, struct Image  *, WORD, WORD );
extern struct TextFont *OpenDiskFont( struct TextAttr * );
extern void CloseFont(struct TextFont *);
extern void SetFont(struct RastPort *, struct TextFont *);

void StripIntuiMessages( struct MsgPort *mp, struct Window *win );
void CloseWindowSafely( struct Window *win );

/*
**  DATA
*/

#define NS_VIEWMODE  HIRES|LACE
#define NS_TYPE      CUSTOMSCREEN|SCREENQUIET|AUTOSCROLL  /* V36+ ?! */
#define NS_DEPTH     4  /* Bitplanes for 16 EGA colors */

#if defined(NOSCREEN)
#define NW_FLAGS     WINDOWSIZING|RMBTRAP|REPORTMOUSE|NOCAREREFRESH|ACTIVATE|WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH
#define NW_IDCMP     VANILLAKEY|MOUSEBUTTONS|MOUSEMOVE|NEWSIZE|INTUITICKS|CLOSEWINDOW
#define NW_TYPE      WBENCHSCREEN
#else /* !NOSCREEN */
#define NW_FLAGS     BORDERLESS|WINDOWSIZING|RMBTRAP|REPORTMOUSE|NOCAREREFRESH|ACTIVATE/*|BACKDROP*/
#define NW_IDCMP     VANILLAKEY|MOUSEBUTTONS|MOUSEMOVE|NEWSIZE|INTUITICKS/*|CLOSEWINDOW*/
#define NW_TYPE      CUSTOMSCREEN
#endif /* !NOSCREEN */

static UBYTE DisplayTitle[]= "Astrolog";

/*
 *  Why the hell do people still use Kick1.3 ?
 */

static struct NewScreen ns = {
  0,0,               /* LeftEdge, TopEdge      */
  724,480,           /* Width, Height          */
  NS_DEPTH,          /* Depth (16 EGA Colors)  */
  0,0,               /* DetailPen, BlockPen    */
  NS_VIEWMODE,       /* ViewModes              */  /* <graphics/view.h> */
  NS_TYPE,           /* Type                   */
  NULL,              /* Font                   */
  DisplayTitle,      /* DefaultTitle           */
  NULL,              /* Gadgets                */
  NULL,              /* CustomBitMap           */
};

static struct NewWindow nw = {
  0,0,               /* LeftEdge, TopEdge      */
  724,480,           /* Width, Height          */
  1,2,               /* DetailPen, BlockPen    */
  NW_IDCMP,          /* IDCMP Flags            */
  NW_FLAGS,          /* Flags                  */
  NULL,              /* FirstGadget            */
  NULL,              /* CheckMark              */
  NULL,              /* Title                  */
  NULL,              /* Screen                 */
  NULL,              /* BitMap                 */
  -1,-1,             /* MinWidth, MinHeight    */
  -1,-1,             /* MaxWidth, MaxHeight    */
  NW_TYPE,           /* Type                   */
};

/* EGA color table */

static UWORD ega_rgb4[] = {
 0x000, /* black      */
 0xA00, /* maroon     */
 0x0A0, /* dark green */
 0xA60, /* orange     */
 0x00A, /* dark blue  */
 0xA0A, /* purple     */
 0x0AA, /* dark cyan  */
 0xBBB, /* light gray */
 0x666, /* dark gray  */
 0xF00, /* red        */
 0x0F0, /* green      */
 0xFF0, /* yellow     */
 0x00F, /* blue       */
 0xF0F, /* magenta    */
 0x0FF, /* cyan       */
 0xFFF, /* white      */
};

/*
 *  AmigaSetPen will use the EGA pen number as an index in the egapen[] array
 *  in order to obtain the (best) corresponding Amiga pen.
 *  If bestpen[p] is >= 0 then we have *allocated* this pen and have to release
 *  it in AmigaDisplayExit().
 */

static int egapen[16]  = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
static int bestpen[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
ULONG *DiskfontBase= (ULONG *)0;
LONG __stack = 8192;

/*
**  32x32x4 icon image created from `astrolog.ico'
*/

static __chip const USHORT ico_data[] = {
   0xe000, 0x0000,    /* ###............................. */
   0xf800, 0x0000,    /* #####........................... */
   0xfe20, 0x0000,    /* #######...#..................... */
   0x6f00, 0x0004,    /* .##.####.....................#.. */
   0x7380, 0x000e,    /* .###..###...................###. */
   0x31cf, 0xf004,    /* ..##...###..########.........#.. */
   0x38bf, 0xfc00,    /* ..###...#.############.......... */
   0x1c7f, 0xfe00,    /* ...###...##############......... */
   0x0eff, 0xff00,    /* ....###.################........ */
   0x07ff, 0xff80,    /* .....####################....... */
   0x07ff, 0xffc0,    /* .....#####################...... */
   0x03ff, 0xffc0,    /* ......####################...... */
   0x07ff, 0xffe0,    /* .....######################..... */
   0x07ff, 0xffe0,    /* .....######################..... */
   0x07ff, 0xffe0,    /* .....######################..... */
   0x07ff, 0xffe0,    /* .....######################..... */
   0x83ff, 0xffe0,    /* #.....#####################..... */
   0x01ff, 0xffe0,    /* .......####################..... */
   0x00ff, 0xffe0,    /* ........###################..... */
   0x00ff, 0xffe0,    /* ........###################..... */
   0x00ff, 0xffc0,    /* ........##################...... */
   0x01ff, 0xffc0,    /* .......###################...... */
   0x01ff, 0xffa2,    /* .......##################.#...#. */
   0x00ff, 0xff70,    /* ........################.###.... */
   0x207f, 0xfe38,    /* ..#......##############...###... */
   0x503f, 0xff1c,    /* .#.#......##############...###.. */
   0x200f, 0xf78c,    /* ..#.........########.####...##.. */
   0x0000, 0x01ce,    /* .......................###..###. */
   0x0000, 0x00f6,    /* ........................####.##. */
   0x0002, 0x007f,    /* ..............#..........####### */
   0x0000, 0x001f,    /* ...........................##### */
   0x0000, 0x0007,    /* .............................### */

   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */
   0x0020, 0x0000,    /* ..........#..................... */
   0x0000, 0x0004,    /* .............................#.. */
   0x0000, 0x000e,    /* ............................###. */
   0x000f, 0xf004,    /* ............########.........#.. */
   0x003f, 0xfc00,    /* ..........############.......... */
   0x007f, 0xfe00,    /* .........##############......... */
   0x00ff, 0xff00,    /* ........################........ */
   0x00ff, 0xff80,    /* ........#################....... */
   0x00ff, 0xffc0,    /* ........##################...... */
   0x007f, 0xffc0,    /* .........#################...... */
   0x063f, 0xffe0,    /* .....##...#################..... */
   0x071f, 0xffe0,    /* .....###...################..... */
   0x079f, 0xffe0,    /* .....####..################..... */
   0x078f, 0xffe0,    /* .....####...###############..... */
   0x83c7, 0xffe0,    /* #.....####...##############..... */
   0x1de3, 0xffe0,    /* ...###.####...#############..... */
   0x3ef1, 0xffe0,    /* ..#####.####...############..... */
   0x3ef8, 0xffe0,    /* ..#####.#####...###########..... */
   0x3efc, 0x7fc0,    /* ..#####.######...#########...... */
   0x1dfe, 0x1fc0,    /* ...###.########....#######...... */
   0x01ff, 0x0f82,    /* .......#########....#####.....#. */
   0x00ff, 0xc700,    /* ........##########...###........ */
   0x207f, 0xe000,    /* ..#......##########............. */
   0x503f, 0xf000,    /* .#.#......##########............ */
   0x200f, 0xf000,    /* ..#.........########............ */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */
   0x0002, 0x0000,    /* ..............#................. */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */

   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */
   0x0020, 0x0000,    /* ..........#..................... */
   0x0000, 0x0004,    /* .............................#.. */
   0x0000, 0x000e,    /* ............................###. */
   0x0000, 0x0004,    /* .............................#.. */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x003c,    /* ..........................####.. */
   0x0000, 0x003e,    /* ..........................#####. */
   0x0000, 0x001e,    /* ...........................####. */
   0x0000, 0x005e,    /* .........................#.####. */
   0x0000, 0x012e,    /* .......................#..#.###. */
   0x0000, 0x004c,    /* .........................#..##.. */
   0x0000, 0x00a0,    /* ........................#.#..... */
   0x0000, 0x0040,    /* .........................#...... */
   0x8000, 0x0120,    /* #......................#..#..... */
   0x1c00, 0x0040,    /* ...###...................#...... */
   0x3e00, 0x00a0,    /* ..#####.................#.#..... */
   0x3e00, 0x0040,    /* ..#####..................#...... */
   0x3e00, 0x0100,    /* ..#####................#........ */
   0x1c00, 0x0000,    /* ...###.......................... */
   0x0000, 0x0002,    /* ..............................#. */
   0x0000, 0x0000,    /* ................................ */
   0x2000, 0x0000,    /* ..#............................. */
   0x5000, 0x0000,    /* .#.#............................ */
   0x2000, 0x0000,    /* ..#............................. */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */
   0x0002, 0x0000,    /* ..............#................. */
   0x0000, 0x0000,    /* ................................ */
   0x0000, 0x0000,    /* ................................ */

   0x6000, 0x0040,    /* .##......................#...... */
   0xf000, 0x4000,    /* ####.............#.............. */
   0xdc20, 0x0000,    /* ##.###....#..................... */
   0x6900, 0x0200,    /* .##.#..#..............#......... */
   0x3200, 0x0000,    /* ..##..#......................... */
   0x3009, 0x5000,    /* ..##........#..#.#.#............ */
   0x1802, 0xbc00,    /* ...##.........#.#.####.......... */
   0x1c15, 0x6e00,    /* ...###.....#.#.#.##.###......... */
   0x0e02, 0xbf0c,    /* ....###.......#.#.######....##.. */
   0x0729, 0x5f86,    /* .....###..#.#..#.#.######....##. */
   0x0702, 0xbfce,    /* .....###......#.#.########..###. */
   0x0395, 0x6fc6,    /* ......###..#.#.#.##.######...##. */
   0x01c2, 0xbfee,    /* .......###....#.#.#########.###. */
   0x00e9, 0x57e4,    /* ........###.#..#.#.#.######..#.. */
   0x0062, 0xbfe0,    /* .........##...#.#.#########..... */
   0x0075, 0x6fe0,    /* .........###.#.#.##.#######..... */
   0x803a, 0xbfe0,    /* #.........###.#.#.#########..... */
   0x0c1d, 0x5fe0,    /* ....##.....###.#.#.########..... */
   0x060e, 0xbfe1,    /* .....##.....###.#.#########....# */
   0x0e07, 0x7fe0,    /* ....###......###.##########..... */
   0x0643, 0xb7c0,    /* .....##..#....###.##.#####...... */
   0x0c15, 0xffc0,    /* ....##.....#.#.###########...... */
   0x0002, 0xffa2,    /* ..............#.#########.#...#. */
   0x0029, 0x7f70,    /* ..........#.#..#.#######.###.... */
   0x0002, 0xbe38,    /* ..............#.#.#####...###... */
   0x2015, 0x5f18,    /* ..#........#.#.#.#.#####...##... */
   0x0002, 0xb78c,    /* ..............#.#.##.####...##.. */
   0x0080, 0x01cc,    /* ........#..............###..##.. */
   0x0000, 0x00f6,    /* ........................####.##. */
   0x0000, 0x003b,    /* ..........................###.## */
   0x0000, 0x020f,    /* ......................#.....#### */
   0x0800, 0x0006,    /* ....#........................##. */
};


static struct Image ico = {
  0,                 /* LeftEdge   */
  0,                 /* TopEdge    */
  32,                /* Width      */
  32,                /* Height     */
  4,                 /* Depth      */
  &ico_data[0],      /* ImageData  */
  0x0F,              /* PlanePick  */
  0x00,              /* PlaneOnOff */
  NULL               /* NextImage  */
};

extern struct Image icon_image;  /* xscreen.c */

/*
**  CODE
*/

#define ERR_NO_INTITION 1
#define ERR_NO_GRAPHICS 2
#define ERR_NO_SCREEN   3
#define ERR_NO_WINDOW   4

/* 
 *  These variables are used to save those settings which might
 *  get modified via AmigaDisplayInit().  They are restored by
 *  AmigaDisplayExit()
 */

static int gs_xWin, gs_yWin, gi_xOffset, gi_yOffset, us_fAnsi;

/****** amiga/AmigaDisplayInit ***********************************************
*
*   NAME
*       AmigaDisplayInit -- Initialize an Amiga screen for Astrolog charts
*
*   SYNOPSIS
*       error= AmigaDisplayInit();
*
*       int AmigaDisplayInit(void);
*
*   FUNCTION
*       This function opens an Amiga screen with an EGA palette and a sizable
*       window which is large enough to hold `gs.xWin' pixel columns and
*       `gs.yWin' pixel rows.  If this should fail for any reason, an error
*       code != 0 is returned.
*
*   INPUTS
*       none
*
*   RESULT
*       error - != 0 in case of an error,  0 otherwise
*
*   EXAMPLE
*       #include "astrolog.h"
*
*       \* ... *\
*
*       if( AmigaDisplayInit() == 0 )
*       {
*          \* do some gfx... *\
*
*          AmigaDisplayExit();
*       }
*       else error();
*
*   NOTES
*       Hack: The EGA colors provided for the screen are taken from the 
*       global rgbbmp[] array (see xdata.c).  However, if the `us.fAnsi' flag 
*       is set, a slightly brighter palette is used.
*
*       Note also that this function sets `us.fAnsi' to `fFalse' to make sure
*       that no ANSI characters are used in the graphic chart mode.  The
*       initial value of `us.fAnsi' is resored by AmigaDisplayExit().
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayExit()
*
******************************************************************************
*
*  #define SPREAD(v) ((ULONG)(v)<<24 | (ULONG)(v)<<16 | (ULONG)(v)<<8 | (v))
*
*  SetRGB32(vp,0,SPREAD(255),SPREAD(255),SPREAD(255));
*
*/


/* scale an RGB4 fraction to RGB32 */

static long scale_rgb4(short v4)
{
  long v32= 0;
  int i;

  for(i=0; i<8; i++)
    v32 = (v32<<4) | v4;

  return v32;
}

int AmigaDisplayInit()
{
  struct Screen *s;
  struct Window *w;

  int err= 0;

  /* save initial values */

  gs_xWin = gs.xWin;    gi_xOffset = gi.xOffset;
  gs_yWin = gs.yWin;    gi_yOffset = gi.yOffset;

  /* Astrolog problem:  Ansi characters not defined in the internal vector font */

  us_fAnsi = us.fAnsi;  us.fAnsi = fFalse;

  /*printf("AmigaDisplayInit() requested display geometry: %dx%d\n",gs.xWin,gs.yWin);*/

  if( (IntuitionBase= (struct IntuitionBase *)OpenLibrary("intuition.library",0L)) )
  {
    if( (GfxBase= (struct GfxBase *)OpenLibrary("graphics.library",0L)) )
    {
      /*
       *  Die beiden printf()s zeigen, daß man hier eins addieren muß um am Ende
       *  die tatsächlich gewünschte größe zu erhalten...
       */

      ns.Width  = nw.Width  = gs.xWin +1;
      ns.Height = nw.Height = gs.yWin +1;

      nw.MaxWidth = BITMAPX;      nw.MaxHeight = BITMAPY;
      nw.MinWidth = BITMAPX1;     nw.MinHeight = BITMAPY1;

#if !defined(NOSCREEN)
      /*
      **  Should we better try to clone the Workbench screen?
      */

      if (IntuitionBase->LibNode.lib_Version <= 34)
        s= (struct Screen *)OpenScreen(&ns);

      else /* Kickstart 2.0+ */
      {
        static struct TagItem scrtaglist[] = { {SA_Overscan,OSCAN_TEXT},{TAG_DONE,0} };

        /*ns.Width  = STDSCREENWIDTH;*/
        /*ns.Height = STDSCREENHEIGHT;*/

        s= OpenScreenTagList(&ns,scrtaglist);
      }

      if(s)
      {
        if(us_fAnsi == fFalse) /* hack! */
        {
          /* create a new colormap from `xdata.c' which is slightly darker than the one above */

          int i;
          for(i=0; i<(1<<ns.Depth); i++)
          {
            UWORD r=  (rgbbmp[i] & 0x0000FF)         / 0x10;
            UWORD g= ((rgbbmp[i] & 0x00FF00) >>  8)  / 0x10;
            UWORD b= ((rgbbmp[i] & 0xFF0000) >> 16)  / 0x10;

            ega_rgb4[i]= (r<<8) | (g<<4) | b;
            /*printf("ega_rgb4[%d] = 0x%03x\n",i,ega_rgb4[i]);*/
          }
        }

        ShowTitle(s,FALSE);
        LoadRGB4(&s->ViewPort, ega_rgb4, 1<<(ns.Depth));
        nw.Screen= s;

#else /* NOSCREEN */
      if(1)
      {
        nw.Screen = (struct Screen *)0;
        nw.Type   = WBENCHSCREEN;
        nw.Title  = /*ns.DefaultTitle*/ DisplayTitle;

        s= LockPubScreen( NULL );
#endif /* NOSCREEN */

        if(nw.Width  > s->Width)   nw.Width  = s->Width;
        if(nw.Height > s->Height)  nw.Height = s->Height;

        if( (w= (struct Window *)OpenWindow(&nw)) )
        {
          int xmin= (int)(w->BorderLeft);
          int ymin= (int)(w->BorderTop);
          int xmax= (int)(w->Width  - w->BorderRight  - 1);
          int ymax= (int)(w->Height - w->BorderBottom - 1);

          gi.xOffset = xmin;
          gi.yOffset = ymin;

          gs.xWin = xmax - xmin;
          gs.yWin = ymax - ymin;

#if defined(NOSCREEN)
          UnlockPubScreen( NULL, s );
          s= w->WScreen;
#endif /* NOSCREEN */

          gi.scr = s;
          gi.win = w;
          gi.rp  = w->RPort;  gi.paging = 0;  /* <-- */

          /*printf("AmigaDisplayInit() allocated display geometry: %dx%d\n",gs.xWin,gs.yWin);*/

#if !defined(NOSCREEN)
          /*
          **  SetRast() können wir hier nur deshalb benutzen, weil wir die
          **  Window Border sowieso nicht sehen wollen...
          */

          SetRast(w->RPort, 0L);

#else /* NOSCREEN */
          {
            struct ColorMap *cm= w->WScreen->ViewPort.ColorMap;
            int p;

            for(p=0; p<16; p++)
            {
              long r,g,b;

               r= scale_rgb4( (ega_rgb4[p] & 0x0F00) >> 8 );
               g= scale_rgb4( (ega_rgb4[p] & 0x00F0) >> 4 );
               b= scale_rgb4( (ega_rgb4[p] & 0x000F)      );

               bestpen[p]= ObtainBestPen( cm, r,g,b, OBP_Precision, PRECISION_GUI, TAG_END );

               if(bestpen[p] >= 0)
                 egapen[p]= bestpen[p];
            }
          }
#endif /* NOSCREEN */
        }

        else /* OpenWindow() failed */
        {
#if !defined(NOSCREEN)
          CloseScreen(s);
#endif /* !NOSCREEN */
          err= ERR_NO_WINDOW;
        }
      }
      else /* OpenScreen() failed */
        err= ERR_NO_SCREEN;
    }
  }

  return err;
}

/****** amiga/AmigaDisplayPaging *********************************************
*
*   NAME
*       AmigaDisplayPaging -- Allocate or free the page for animations
*
*   SYNOPSIS
*       paging= AmigaDisplayPaging();
*
*       int AmigaDisplayPaging(void);
*
*   FUNCTION
*       Here we allocate a 2nd rastport for double buffering.  All Amiga
*       drawing happens in `gi.rp' and here we replace `gi.rp' with a newly
*       allocated rastport which has the same size as the one of the astrolog
*       screen `gi.scr'.  Since paging is mainly used for animation purposes, 
*       we also add the INTUITICKS bit to our window's IDCMP port.
*       Failure due to low memory is no fatal error because double buffering is
*       only a bonus for the animations.
*
*   INPUTS
*       none
*
*   RESULT
*       paging - 0 if we disabled paging with this call, !=0 otherwise.
*
*   EXAMPLE
*       #include "astrolog.h"
*
*       \* ... *\
*
*       if( AmigaDisplayInit() == 0 )
*       {
*         \* switch to double buffering mode *\
*
*         if(!gi.paging)
*           AmigaDisplayPaging();
*
*         \* do some anim stuff... *\
*
*         AmigaDisplayExit();
*       }
*       else error();
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayPaging(), AmigaDisplayExit()
*
******************************************************************************
*
*
*/

int AmigaDisplayPaging(void)
{
  if(gi.paging)
  {
    rp_dispose(gi.rp);
    gi.rp= (gi.win)->RPort;
    gi.paging= 0;

    ModifyIDCMP(gi.win, (gi.win->IDCMPFlags) & ~INTUITICKS);
  }
  else /* !gi.paging */
  {
    struct RastPort *rp= rp_new(gi.scr->Width, gi.scr->Height, gi.scr->RastPort.BitMap->Depth);

    if(rp)
    {
      gi.rp= rp;
      gi.paging= 1;

      ModifyIDCMP(gi.win, (gi.win->IDCMPFlags) | INTUITICKS);
    }
  }
  return gi.paging;
}


/****** amiga/AmigaDisplayExit ***********************************************
*
*   NAME
*       AmigaDisplayExit -- Dispose Astrolog's Amiga graphics stuff
*
*   SYNOPSIS
*       AmigaDisplayExit();
*
*       void AmigaDisplayExit(void);
*
*   FUNCTION
*       This function closes Astrolog's window and screen which had been
*       provided via AmigaDisplayInit().  If paging is on, i.e. a 2nd
*       page has been provided for animations, then this page is disposed
*       as well.
*
*   INPUTS
*       none
*
*   RESULT
*       none
*
*   EXAMPLE
*       #include "astrolog.h"
*
*       \* ... *\
*
*       if( AmigaDisplayInit() == 0 )
*       {
*          \* do some gfx... *\
*
*          AmigaDisplayExit();
*       }
*       else error();
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayInit()
*
******************************************************************************
*
*
*/

void AmigaDisplayExit()
{
  if(gi.paging)
  {
    AmigaDisplayPaging();
  }

#if defined(NOSCREEN)
  {
    struct ColorMap *cm= (gi.win)->WScreen->ViewPort.ColorMap;
    int p;

    for(p=0; p<16; p++)
      if(bestpen[p] >= 0)
        ReleasePen(cm,bestpen[p]);
  }
#endif /* NOSCREEN */

  if(gi.win)
  {
    /*CloseWindow(gi.win);*/
    CloseWindowSafely(gi.win);
    gi.win= (struct Window *)0;
  }

#if !defined(NOSCREEN)
  if(gi.scr)
  {
    CloseScreen(gi.scr);
    gi.scr= (struct Screen *)0;
  }
#endif /* !NOSCREEN */

  if(GfxBase)
  {
    CloseLibrary((struct Library *)GfxBase);
    GfxBase= (struct GfxBase *)0;
  }

  if(IntuitionBase)
  {
    CloseLibrary((struct Library *)IntuitionBase);
    IntuitionBase= (struct IntuitionBase *)0;
  }

  /* restore initial values */

  gs.xWin = gs_xWin;    gi.xOffset = gi_xOffset;
  gs.yWin = gs_yWin;    gi.yOffset = gi_yOffset;

  us.fAnsi = us_fAnsi;
}

/****** amiga/AmigaDisplayUpdate *********************************************
*
*   NAME
*       AmigaDisplayUpdate -- Display the temporary drawing page
*
*   SYNOPSIS
*       AmigaDisplayUpdate();
*
*       void AmigaDisplayUpdate(void);
*
*   FUNCTION
*       If double buffering is enabled, then the hidden page (which is the
*       one we use to paint on) will be copied to the currently visible page.
*       Without any 2nd page, calling this procedure is a no-op.
*
*   INPUTS
*       none
*
*   RESULT
*       none
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayPaging()
*
******************************************************************************
*
*
*/

void AmigaDisplayUpdate(void)
{
  if(gi.paging)
    ClipBlit(gi.rp, 0,0, gi.win->RPort, 0,0, gi.win->Width,gi.win->Height, 0xc0);
}


/****** amiga/AmigaClearScreen ***********************************************
*
*   NAME
*       AmigaClearScreen -- Clear Astrolog's Amiga graphics display
*
*   SYNOPSIS
*       AmigaClearScreen();
*
*       void AmigaClearScreen(void);
*
*   FUNCTION
*       This procedure unsets all pixels in the drawing page.
*
*   INPUTS
*       none
*
*   RESULT
*       none
*
*   EXAMPLE
*
*   NOTES
*       Calling this function is usually faster than painting a rectangle
*       covering the whole screen.
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayPaging()
*
******************************************************************************
*
*
*/

void AmigaClearScreen(void)
{

#if defined(NOSCREEN)
  if(gi.win && gi.rp)
  {
    struct RastPort *rp= gi.rp;
    struct Window *w= gi.win;

    SHORT xmin= (SHORT)(w->BorderLeft);
    SHORT ymin= (SHORT)(w->BorderTop);
    SHORT xmax= (SHORT)(w->Width  - w->BorderRight  - 1);
    SHORT ymax= (SHORT)(w->Height - w->BorderBottom - 1);

    SetAPen(rp,0L);
    RectFill(rp, xmin,ymin, xmax,ymax);
  }
#else /* !NOSCREEN */
  SetRast(gi.rp,0L);
#endif /* !NOSCREEN */
}

/****** amiga/AmigaDisplayResize *********************************************
*
*   NAME
*       AmigaDisplayResize -- Guarantee certain graphic display dimensions
*
*   SYNOPSIS
*       error= AmigaDisplayResize(width, height);
*
*       int AmigaDisplayResize(int, int);
*
*   FUNCTION
*       This function makes sure that the Amiga graphics page (screen and
*       window) are large enough to hold a graphic with `width' pixels 
*       horizontally and `height' pixels vertically.  If the current screen
*       is large enough, nothing happens.  Otherwise it will be enlarged
*       to the given size.
*
*   INPUTS
*       none
*
*   RESULT
*       error - != 0 in case of an error,  0 otherwise
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*       AmigaDisplayInit(), AmigaDisplayExit()
*
******************************************************************************
*
*
*/

int AmigaDisplayResize(int width, int height)
{
  int err= 0;

  /* We only have to close screen and window if we must enlarge the display */

  if( (width > (gi.win)->Width) || (height > (gi.win)->Height) )
  {
    int p= gi.paging;

    AmigaDisplayExit();

    if(width  > gs.xWin)  gs.xWin= width;
    if(height > gs.yWin)  gs.yWin= height;

    err= AmigaDisplayInit();

    if(!err && p)
      AmigaDisplayPaging();
  }

  /*
  **  Now that we can be sure that the display is large enough, we fake
  **  the exact desired dimensions
  */

  gs.xWin= width;
  gs.yWin= height;

  return err;
}

/****** amiga/AmigaGetKey ****************************************************
*
*   NAME
*       AmigaGetKey -- Wait for a keypress and return it's ASCII code
*
*   SYNOPSIS
*       key= AmigaGetKey();
*
*       int AmigaGetKey(void);
*
*   FUNCTION
*       Wait for a keypress and return the ASCII code of that key.  This 
*       function distinguishes between console mode and graphics mode and
*       calls getkey() if there is no graphics window.
*
*   INPUTS
*       none
*
*   RESULT
*       key   - The ASCII code of the character on the pressed key
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*       getkey()
*
******************************************************************************
*
*
*/

int AmigaGetKey(void)
{
  int key= '\0';

  if(gi.win)
  {
    struct Window *w= gi.win;

    while(!key)
    {
      ULONG sig= Wait( SIGBREAKF_CTRL_C | (1L<<w->UserPort->mp_SigBit) );

      if( sig & SIGBREAKF_CTRL_C )
        key= '\003';

      if( sig & (1L<<w->UserPort->mp_SigBit) )
      {
        struct IntuiMessage *imsg;

        while( (imsg=(struct IntuiMessage *)GetMsg(w->UserPort)) )
        {
          ULONG  class  = imsg->Class;
          USHORT code   = imsg->Code;

          ReplyMsg((struct Message *)imsg);

          switch(class)
          {
            case VANILLAKEY:
              key= (int)code;
              break;
          }
        }
      }
    }
  }
  else /* !gi.win */
    key= getkey();

  return key;
}


/*
**  undocumented hacks...
*/

void AmigaSetPen(int pen) { SetAPen(gi.rp,egapen[pen]); }

int AmigaMaxDisplayRows(void)
{
  int result= 2730;

  if(gi.scr)
  {
    result= (gi.scr)->Height;
  }

  else /* !gi.scr */
  {
    if(IntuitionBase)
    {
      struct Screen *s= LockPubScreen( NULL );

      if(s)
        result= s->Height;

      UnlockPubScreen( NULL, s );
    }
  }

  return result;
}


int AmigaMaxDisplayColumns(void)
{
  int result= 2730;

  if(gi.scr)
  {
    result= (gi.scr)->Width;
  }

  else /* !gi.scr */
  {
    if(IntuitionBase)
    {
      struct Screen *s= LockPubScreen( NULL );

      if(s)
        result= s->Width;

      UnlockPubScreen( NULL, s );
    }
  }

  return result;
}



char *AmigaGetString(char *buf)
{
  int key, n=0;

  do
  {
    key= AmigaGetKey();

    if(32 <= key && key <= 127)
    {
      buf[n++]= key;
      xPrint("%c",key);
    }

    else if(key=='\b')
    {
      if(n>0)
      {
        int pen= gi.kiCur;

        --n;

        DrawColor(gi.kiOff);
        xPrint("\b%c\b",buf[n]);
        DrawColor(pen);
      }
      else DisplayBeep(gi.scr);
    }

    else if(key=='\n' || key=='\r')
    {
      buf[n]= '\0';
    }
    else DisplayBeep(gi.scr);

  } while(key!='\n' && key!='\r');

  return buf;
}


/**/

int AmigaDisplayDebug(void)
{
  AmigaClearScreen();
  DrawImage(gi.rp, &ico, gs.xWin-ico.Width, 0);
  /*DrawImage(gi.rp, &icon_image, gs.xWin-icon_image.Width,100);*/

  xPrintAt(1,1,"");

  /*
   *  Who is the first to blame me for this abuse of the `-k' switch (-;
   */

  if(us_fAnsi)
  {
    DiskfontBase= (struct DiskfontBase *)OpenLibrary("diskfont.library",0L);

    if(DiskfontBase)
    {
      static struct TextAttr ta = { "CGTimes.font", 70, FS_NORMAL, FPF_DISKFONT };
      struct TextFont *tf;

      xPrintAt(1,1,"{green}Computing `{white}%s{green}' ({white}%d{green}) ... Please Wait ...",ta.ta_Name,ta.ta_YSize);

      if( (tf= OpenDiskFont(&ta)) )
      {
        struct RastPort *rp= gi.rp;
        char *str= "Astrolog";
        int L= strlen(str);
        int l,x,y;

        xPrintAt(1,1,"{black}Computing `%s' (%d) ... Please Wait ...",ta.ta_Name,ta.ta_YSize);

        DrawImage(rp, &ico, 0,0);
        DrawImage(rp, &ico, gs.xWin-ico.Width, 0);

        SetFont(rp,tf);

        l= TextLength(rp,str,L);
        x= (gs.xWin-l)/2;
        y= tf->tf_Baseline+1;

        /* smoothing via outline */

        DrawColor(kLtGray);  SetDrMd(gi.rp,JAM1);
        /*Move(rp, x-1,y-1); Text(rp,str,L);*/
        Move(rp, x  ,y-1); Text(rp,str,L);
        Move(rp, x+1,y-1); Text(rp,str,L);
        Move(rp, x-1,y  ); Text(rp,str,L);
        Move(rp, x+1,y  ); Text(rp,str,L);
        Move(rp, x-1,y+1); Text(rp,str,L);
        Move(rp, x  ,y+1); Text(rp,str,L);
        /*Move(rp, x+1,y+1); Text(gi.rp,str,L);*/

        DrawColor(kWhite);
        Move(gi.rp, x,y);
        Text(gi.rp,str,L);

        DrawColor(kRed);
        DrawLine(0,tf->tf_YSize+5, gs.xWin,tf->tf_YSize+5);

        CloseFont(tf);

        xPrintAt(9,1,"");
      }
      else
        xPrint(" {red}failed.\n\n");

      CloseLibrary(DiskfontBase);
    }
    else xPrintAt(1,1,"{red}Warning: could not open {white}diskfont.library{red}!\n");
  }

  xPrint("{white}** %s version %s **\n", szAppName, szVersionCore);
  xPrint("{ltgray}As of %s  (compiled %s, %s)\n", szDateCore, __DATE__, __TIME__);
  xPrint("{cyan}By Walter D. Pullen (%s)\n", szAddressCore);
  xPrint("{yellow}Amiga port by Tobias Ferber (tf@antares.ping.de)\n");

  xPutString("\n\n");

  {
    /* Graphics chart modes */

    static char *gcm[] = {
      "<unknown>",   /*  0 */
      "gWheel",      /*  1 */
      "gHouse",      /*  2 */
      "gGrid",       /*  3 */
      "gHorizon",    /*  4 */
      "gOrbit",      /*  5 */
      "gAstroGraph", /*  6 */
      "gEphemeris",  /*  7 */
      "gWorldMap",   /*  8 */
      "gGlobe",      /*  9 */
      "gPolar",      /* 10 */
      "gBiorhythm",  /* 11 */
      "gCalendar",   /* 12 */
      "gDisposit",   /* 13 */
    };

    xPrint("{red}Internal settings page called from graphic chart mode {white}%d{red} ({white}%s{red})\n",
      gi.nMode, gcm[gi.nMode]);
  }

  xPutString("\n");

  {
    struct Window *w= gi.win;

    xPrint("{green}Current window geometry: {white}%d{green}x{white}%d{green}x{white}%d"
           "{green}, borders (left,top,right,bottom): {white}%d{green},{white}%d{green},{white}%d{green},{white}%d\n",
      w->Width, w->Height, w->RPort->BitMap->Depth,
      w->BorderLeft, w->BorderTop, w->BorderRight, w->BorderBottom);

    xPrint("{green}Resulting maximum playfield: ({white}%d{green},{white}%d{green}) .. ({white}%d{green},{white}%d{green}) [geometry {white}%d{green}x{white}%d{green}]\n",
      w->BorderLeft,
      w->BorderTop,
      w->Width  - w->BorderRight  - 1,
      w->Height - w->BorderBottom - 1,
      w->Width  - w->BorderRight  - 1 - w->BorderLeft,
      w->Height - w->BorderBottom - 1 - w->BorderTop);

    xPrint("{green}Actually used playfield: ({white}%d{green},{white}%d{green}) .. ({white}%d{green},{white}%d{green})  [geometry {white}%d{green}x{white}%d{green}]\n",
      gi.xOffset,gi.yOffset, gs.xWin,gs.yWin,gs.xWin-gi.xOffset,gs.yWin-gi.yOffset);

    xPrint("{green}Paging is {white}%s{green} for animation mode {white}%d{green}.\n",
      gi.paging ? "enabled" : "disabled", gs.nAnim);
  }

  xPutString("\n");

  {
    xPrint("{green}"
           "EGA Colors: "  "{black}"     "black,   "
                           "{maroon}"    "maroon,  "
                           "{dkgreen}"   "dkgreen, "
                           "{orange}"    "orange,  "
                           "{dkblue}"    "dkblue,  "
                           "{purple}"    "purple,  "
                           "{dkcyan}"    "dkcyan,  "
                           "{ltgray}"    "ltgray,  "  "\n"
           "            "  "{dkgray}"    "dkgray,  "
                           "{red}"       "red,     "
                           "{green}"     "green,   "
                           "{yellow}"    "yellow,  "
                           "{blue}"      "blue,    "
                           "{magenta}"   "magenta, "
                           "{cyan}"      "cyan,    "
                           "{white}"     "white.   "  "\n");
  }

  xPutString("\n");

  {
    ULONG chip= AvailMem(MEMF_CHIP);
    ULONG fast= AvailMem(MEMF_FAST);

    xPrint("{cyan}Available memory: {white}%ld{cyan} bytes (CHIP: {white}%ld{cyan}, FAST: {white}%ld{cyan})\n",
      chip,fast,chip+fast);

    chip= AvailMem(MEMF_CHIP|MEMF_LARGEST);
    fast= AvailMem(MEMF_FAST|MEMF_LARGEST);

    xPrint("{cyan}Largest available memory block: {white}%ld{cyan} bytes CHIP, {white}%ld{cyan} bytes FAST.\n",
      chip,fast);                                         
  }

  xPutString("\n");

  if(!gi.paging)
  {
    /*
    **  Taken from CommandLineX()
    */

    char szCommandLine[cchSzMax], *rgsz[MAXSWITCHES];
    int argc, fT;

    ciCore = ciMain;
    fT = us.fLoop; us.fLoop = fTrue;
    argc = NPromptSwitches(szCommandLine, rgsz);
    is.cchRow = 0;
    is.fSzInteract = fTrue;

    if( FProcessSwitches(argc, rgsz) )
    {
      is.fMult = fFalse;
      FPrintTables();

      if (is.fMult)
      {
        ClearB((lpbyte)&us.fCredit, (int)((lpbyte)&us.fLoop - (lpbyte)&us.fCredit));
      }
    }

    is.fSzInteract = fFalse;
    us.fLoop = fT;
    ciMain = ciCore;
  }

  xPutString("{magenta}Press any key to continue (or any other to resume) ...");
  AmigaDisplayUpdate();
  return AmigaGetKey();
}

/* Generic NDK Code */

#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/ports.h"
#include "intuition/intuition.h"

/*
 * Remove and reply all IntuiMessages on a port that have been sent to a particular window
 * ( note that we don't rely on the ln_Succ pointer of a message after we have replied it )
 */

void
StripIntuiMessages( mp, win )
struct MsgPort *mp;
struct Window *win;
{
  struct IntuiMessage *msg;
  struct Node *succ;

  msg= (struct IntuiMessage *)mp->mp_MsgList.lh_Head;

  while( (succ= msg->ExecMessage.mn_Node.ln_Succ) )
  {
    if( msg->IDCMPWindow ==  win )
    {
      /* Intuition is about to free this message.
       * Make sure that we have politely sent it back.
       */
      Remove( (struct Node *)msg );
      ReplyMsg( (struct Message *)msg );
    }

    msg= (struct IntuiMessage *)succ;
  }
}

/*
 * These functions close an Intuition window that shares a port with other Intuition
 * windows or IPC customers.
 *
 * We are careful to set the UserPort to null before closing, and to free
 * any messages that it might have been sent.
 */

void
CloseWindowSafely( struct Window *win )
{
  /* we forbid here to keep out of race conditions with Intuition */
  Forbid();

  /* send back any messages for this window
   * that have not yet been processed
   */
  StripIntuiMessages( win->UserPort, win );

  /* tell Intuition to stop sending more messages */
  ModifyIDCMP( win, 0L );

  /* turn multitasking back on */
  Permit();

  /* and really close the window */
  CloseWindow( win );
}
