/* Screen display routines              */
/* written by Günther Röhrich           */
/* this source is Public Domain         */

/* this works only with OS 3.0 or higher */

#include <stdio.h>

#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/modeid.h>


#ifndef __GNUC__
#include <pragmas/intuition_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#else
#include <inline/graphics.h>  
#include <inline/intuition.h> 
#include <inline/exec.h>
#endif

#define HAM8 1

#pragma regcall(newWPL8(d2,a2))
#pragma regcall(newWPL8Pos(a0,d0,d1,d2,a2))

#ifdef __GNUC__
extern __inline void
newWPL8(unsigned long width, UBYTE *array)
{
  register unsigned long d2 __asm("d2") = width;
  register UBYTE *a2 __asm("a2") = array;
  __asm __volatile ("jsr _newWPL8"
  : /* no output */
  : "r" (d2), "r" (a2)
  : "a0","a1","a2","d0","d1","d2", "memory");
}
extern __inline void
newWPL8Pos(struct RastPort *rp, unsigned long xstart, unsigned long ystart,
        unsigned long width, UBYTE *array)
{
  register struct RastPort *a0 __asm("a0") = rp;
  register unsigned long d0 __asm("d0") = xstart;
  register unsigned long d1 __asm("d1") = ystart;
  register unsigned long d2 __asm("d2") = width;
  register UBYTE *a2 __asm("a2") = array;
  __asm __volatile ("jsr _newWPL8Pos"
  : /* no output */
  : "r" (a0), "r" (d0), "r" (d1), "r" (d2), "r" (a2)
  : "a0","a1","a2","d0","d1","d2", "memory");
}
#endif

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
extern int VGAenable; /* indicates if user wants to have VGA mode */
extern int DisplayOriginal;
extern int OverView; /* indicates if Overview */
struct BitMap *WPBitMap = NULL;
struct BitPlane *WPPlane = NULL;
static void error_exit(const char *msgtext);
struct BitMap *my_bitmap = NULL;
struct Screen *my_screen = NULL;
struct Window *my_window = NULL;
struct RastPort temprp; /* a copy of the screen's RastPort */
int Drow = 0; 
extern ULONG *ScreenColorTable;

/* for newWPL8() */
PLANEPTR PlanePos[8];
UBYTE    *linebuf = NULL;

struct TextAttr MyTextAttr = {
"topaz.font", 8, 0, FPF_ROMFONT};

struct TagItem MyScreenTags[] = {

{ SA_Width,     (ULONG)0 },
{ SA_Height,    (ULONG)0 },
{ SA_Depth,     (ULONG)0 },
{ SA_DisplayID, (ULONG)0 },
{ SA_Colors32,  (ULONG)NULL},
{ SA_BitMap,    (ULONG)NULL},
{ SA_Font,      &MyTextAttr},
{ SA_Type,      (ULONG)CUSTOMSCREEN|CUSTOMBITMAP },
{ SA_Quiet,     (ULONG)TRUE },
{ SA_AutoScroll,(ULONG)TRUE },
{ SA_Overscan,  (ULONG)OSCAN_STANDARD },
{ TAG_DONE,     (ULONG)TRUE }  };

struct TagItem MyWindowTags[] = {

{ WA_Width,        (ULONG)0 },
{ WA_Height,       (ULONG)0 },
{ WA_CustomScreen, (ULONG)NULL },
{ WA_Activate,     (ULONG)TRUE },
{ WA_Left,         (ULONG)0 },
{ WA_Top,          (ULONG)0 },
{ WA_NoCareRefresh,(ULONG)TRUE },
{ WA_Borderless,   (ULONG)TRUE },
{ WA_Backdrop,     (ULONG)TRUE },
{ WA_RMBTrap,      (ULONG)TRUE }, /* disable screen menu drawing */
{ WA_IDCMP,        (ULONG)IDCMP_MOUSEBUTTONS | IDCMP_VANILLAKEY },
{ WA_BusyPointer,  (ULONG)TRUE }, /* V39 only! */
{ TAG_DONE,        (ULONG)TRUE } };

void CloseDisplay(void);

/* Initialize the display, colors will be set later */

int InitDisplay(int cols, int rows, ULONG Mode, int NumPlanes)
{
  ULONG Depth, DisplayID;

  if(!(IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 39)))
   error_exit("Can't open intuition.library V39 or higher");

  if(!(GfxBase = OpenLibrary((UBYTE *)"graphics.library",39)))
   error_exit("Can't open graphics.library V39 or higher");

  
  /* Calculate a DisplayID */
  /* this should be done better... */


  if(VGAenable)
  {
    if(Mode == HAM8)
    {
      if(cols > 384 || rows > 384)
        DisplayID = VGAPRODUCTHAM_KEY;
      else
        DisplayID = VGALORESHAMDBL_KEY;
    }
    else
    {
      if(cols > 384 || rows >384) 
        DisplayID = VGAPRODUCT_KEY;
      else
        DisplayID = VGALORESDBL_KEY;        
    }
  }
  else
  {  
    if(Mode == HAM8)
    {
      if(cols > 384 || rows > 384)
        DisplayID = HIRESHAMLACE_KEY;
      else
        DisplayID = HAM_KEY;
    }
    else
    {
      if(cols > 384 || rows >384) 
        DisplayID = HIRESLACE_KEY;
      else
        DisplayID = LORES_KEY; 
    }   
  }

  if(Mode == HAM8) Depth = 8;
    else Depth = NumPlanes;

  MyScreenTags[0].ti_Data = (ULONG)cols;
  MyScreenTags[1].ti_Data = (ULONG)rows;
  MyScreenTags[2].ti_Data = (ULONG)Depth;
  MyScreenTags[3].ti_Data = (ULONG)DisplayID;
  MyScreenTags[4].ti_Data = (ULONG)((void *)ScreenColorTable);

  MyWindowTags[0].ti_Data = (ULONG)cols;
  MyWindowTags[1].ti_Data = (ULONG)rows;

  my_bitmap = AllocBitMap(((cols+31)>>5)<<5,rows,Depth,BMF_CLEAR|BMF_DISPLAYABLE,NULL);

  if(my_bitmap == NULL) return 0;

  MyScreenTags[5].ti_Data = (ULONG)my_bitmap;
  
  my_screen = OpenScreenTagList(NULL, (struct TagItem *)&MyScreenTags);

  if(my_screen == NULL) return 0;
    
  /* open a dummy window to allow autoscroll feature      */
  
  MyWindowTags[2].ti_Data = (ULONG)((void *)my_screen);
  my_window = OpenWindowTagList(NULL, (struct TagItem *)&MyWindowTags);

  if(my_window == NULL) return 0;
 
  temprp.BitMap = NULL;
 
  if(DisplayOriginal)
  {
    /* initialize temprp for use with WritePixelLine8() */

    CopyMem(&my_screen->RastPort, &temprp, sizeof(struct RastPort));
    temprp.Layer = NULL;
    /* V39 function */
    temprp.BitMap = AllocBitMap(cols, 1, my_screen->RastPort.BitMap->Depth, 0, my_screen->RastPort.BitMap);

    if(temprp.BitMap == NULL) return 0;
  }
  else
  {
    /* Allocate line buffer */
    linebuf = AllocVec((ULONG)cols+32L, 0);
    if(linebuf == NULL) return 0;
  
    /* initialize WPBitMap for use with newWPL8Pos */
    if(OverView)
    {
      WPBitMap = AllocBitMap(2048, 1, 8, BMF_DISPLAYABLE|BMF_INTERLEAVED, NULL);
      if(WPBitMap == NULL) return 0;
      WPPlane = WPBitMap->Planes[0];
    }
  }

  /* set pens and drawing modes */

  if(Depth == 8)
  {
    SetAPen(&my_screen->RastPort, 255);
    SetDrMd(&my_screen->RastPort, JAM1);
  }


  return 1; /* success */  
}


/* Close the display */

void CloseDisplay(void)
{
  if(my_screen) ScreenToBack(my_screen);

  if(temprp.BitMap)
  {
    /* V39 function */
    FreeBitMap(temprp.BitMap);
    temprp.BitMap = NULL;
  }

  if(my_window)
  {
    CloseWindow(my_window);
    my_window = NULL;
  }
  if(my_screen)
  {
    CloseScreen(my_screen);
    my_screen = NULL;
  }
  if(my_bitmap)
  {
    FreeBitMap(my_bitmap);
    my_bitmap = NULL;
  }
  if(linebuf)
  {
    FreeVec(linebuf);
    linebuf = NULL;
  }
  if(WPBitMap)
  {
    FreeBitMap(WPBitMap);
    WPBitMap = NULL;
  }
  if(IntuitionBase)
  {
   CloseLibrary(IntuitionBase);
   IntuitionBase = NULL;
  }
  if(GfxBase)
  {
    CloseLibrary(GfxBase);
    GfxBase = NULL;
  }
}


/* display a line of chunky pixel graphics... */


void DisplayRow(char *array, int cols)
{
  if(my_screen)
  {
    if(DisplayOriginal)
    {
      WritePixelLine8(&my_screen->RastPort, 0, Drow, cols, array, &temprp);
    }
    else
    {   
      int j;
      struct RastPort *MyRastPtr;
      MyRastPtr = &my_screen->RastPort;
      for(j=0; j<8; j++)
        PlanePos[j]=MyRastPtr->BitMap->Planes[j] + MyRastPtr->BitMap->BytesPerRow*Drow;
      newWPL8(cols, (UBYTE *)array);
    }
    Drow++;
  }
}

void DisplayRowPos(char *array, int cols, int xpos, int ypos)
{
  if(my_screen)
  {
    if(DisplayOriginal)
    {
      WritePixelLine8(&my_screen->RastPort, (ULONG)xpos, (ULONG)ypos, cols, array, &temprp);
    }
    else
    {
      newWPL8Pos(&my_screen->RastPort, (ULONG)xpos, (ULONG)ypos, cols, (UBYTE *)array); 
    }
  }
}


void DisplayNrGray(int nr, int xpos, int ypos)
{
  if(my_screen)
  {
    char String[10];
    sprintf(String, "%d", nr);
    Move(&my_screen->RastPort, (SHORT)xpos, (SHORT)ypos);
    Text(&my_screen->RastPort, String, strlen(String));
  }
} 

/* check for a right mouse button press */

int CheckButton(void)
{
  struct IntuiMessage *msg;
  int Button = 0;

  if(my_window)
  {
    while(msg = (struct IntuiMessage *)GetMsg(my_window->UserPort))
    {
      if(msg->Class == IDCMP_MOUSEBUTTONS)
        if(msg->Code == MENUDOWN)
          Button = 1; 
      ReplyMsg((struct Message *)msg);
      if(Button) break;
    }
  }
  return Button;
}
       
/* final wait after the picture is finished */

void FinalWait(void)
{
  struct IntuiMessage *msg;
  int Button = 0;
  struct TagItem WinPointerSet[] = { {WA_Pointer, (ULONG)NULL}, {TAG_DONE, (ULONG)NULL} };

  /* set the normal pointer */
  /* V39 function */

  /* SetWindowPointer(my_window, WA_Pointer, NULL, TAG_DONE); */
  SetWindowPointerA(my_window, WinPointerSet);
  
  if(my_window)
  {
    while(!Button)
    {
      Wait(1L<<my_window->UserPort->mp_SigBit);
      while(msg = (struct IntuiMessage *)GetMsg(my_window->UserPort))
      {
        if(msg->Class == IDCMP_MOUSEBUTTONS)
          if(msg->Code == MENUDOWN) Button = 1; 
        ReplyMsg((struct Message *)msg);
        if(Button) break;
      }
    }
  }
}
      
static void error_exit(const char *msgtext)
{
 printf("%s", msgtext);
 CloseDisplay();
 exit(10);
}
