/*
 * Title:
 *	display.c
 *
 * Authors:
 *	Michael P. Schenck
 *
 * Purpose:
 *	Contains all the routines needed for opening and closing the 
 *	renderers output screen.  This is a custom screen which can
 *	be opened in a variety of resolutions.  The routines will 
 *	support more than one bitplane of data, but for the wireframe
 *	graphics, no more is needed.
 *	A screen clearing function is provided which clears the bitmap
 *      of the display that is currently hidden.  Output always goes
 *	to the hidden screen.  A line drawing function will draw a
 *	line on the hidden bitplane. 
 *	A getinput function gets a character from the keyboard.  You
 *	can tell the function either to wait or to just see if there
 *	is a character available and continue.
 *	The last function provided is a pause function that waits
 * 	the duration specified (secs, microsecs). 
 * 
 * Copyright Info:
 *	Copyright (C) 1993, 1994 -- by Michael P. Schenck, 
 *	(mps4466@ultb.isc.rit.edu)
 *	
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published
 *	by the Free Software Foundation; either version 2 of the License,
 *	or (at your option) any later version.
 *
 *	This software is distributed in the hope that it will be useful, but
 *	WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *      For a copy of the GNU General Public License
 *	write to the Free Software Foundation, 675 Mass Ave,
 *	Cambridge, MA  02139, USA.
 *
 */

#include <proto/diskfont.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <exec/memory.h> 
#include <stdlib.h>
#include <math.h>
#include "/include/errors.h"
#include "/include/display.h"

  /* System startup structures. */

static struct NewScreen newscreen = {
   0,0,320,200,
   3,0,0,NULL,CUSTOMSCREEN|CUSTOMBITMAP|SCREENQUIET,
   NULL,NULL,
   NULL,NULL
};
   
static struct NewWindow newwindow = {
   0,0,320,200,0,0,VANILLAKEY,
   SIMPLE_REFRESH | ACTIVATE | NOCAREREFRESH | BORDERLESS | RMBTRAP,
   NULL,NULL,NULL,NULL,NULL,
   320,200,320,200,CUSTOMSCREEN
};

 /* Blank pointer data. */

static USHORT chip pointer[22] = {
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,0x0000
};                           
   
static long   __OSlibversion = 33;   
static struct Window *window = NULL;
static struct timerequest timermsg;
static struct MsgPort *treplyport = NULL;
static UBYTE timeropen,depth;
static UWORD *colortable,width,height,code;
static ULONG signalmask,signals,class;

struct Screen *screen = NULL;
struct BitMap *dbbitmaps[2];

static UWORD bytewidth,blitsize;
static UBYTE buffered;

	/* Assembly blitter functions (rasterasm.a). */

void __asm blitclearmem(register __d0 UWORD blitsize,
			register __a0 APTR clear);

void __asm blitline(register __d0 WORD x0,
		    register __d1 WORD y0,
		    register __d2 WORD x1,
		    register __d3 WORD y1,
		    register __d4 UWORD bytewidth,
		    register __a0 APTR firstpixel);

struct BitMap *allocatebitmap(void);
void  releasebitmap(struct BitMap *);
UBYTE getmessage(void);
void cleanup(void);

 /* Opens and initializes the display module. */

UBYTE opendisplay(UWORD *coltable,UWORD swidth,UWORD sheight,UBYTE sdepth)

{  
   width = swidth;
   height = sheight;
   depth = sdepth; 
   if((dbbitmaps[0] = allocatebitmap()) == NULL)
   	return(MEM_ALLOC_FAILURE);
   if((dbbitmaps[1] = allocatebitmap()) == NULL) {
   	releasebitmap(dbbitmaps[0]);
	return(MEM_ALLOC_FAILURE);
   } 
   newscreen.Width = width;
   newscreen.Height = height;
   newscreen.Depth = depth;
   if (width>320)
      newscreen.ViewModes = HIRES;   
   if (height>200)
      newscreen.ViewModes = newscreen.ViewModes | LACE;
   newscreen.CustomBitMap = dbbitmaps[0];
   newwindow.Width = width;
   newwindow.Height = height;
   newwindow.MinWidth = width;
   newwindow.MinHeight = height;
   newwindow.MaxWidth = width;
   newwindow.MaxHeight = height;
   colortable = coltable;
   if ((screen = OpenScreen(&newscreen)) == NULL) {
      cleanup(); 
      return(OPEN_VIEW_FAILURE);
   }  
   screen->RastPort.Flags = DBUFFER;      
   newwindow.Screen = screen;
   if ((window = OpenWindow(&newwindow)) == NULL) {
      cleanup();
      return(OPEN_VIEW_FAILURE);
   }
   LoadRGB4(&(screen->ViewPort),colortable,(UBYTE)pow2((double)depth)); 
   SetPointer(window,pointer,9,16,0,0);   
   timeropen = OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *) &timermsg,0);
   if (timeropen) {
      cleanup();
      return(OPEN_VIEW_FAILURE);
   }
   if ((treplyport = (struct MsgPort *) CreatePort(0,0)) == NULL) {
      cleanup();
      return(OPEN_VIEW_FAILURE);
   }
   signalmask = 1L << window->UserPort->mp_SigBit;

   bytewidth = width >> 3;			/* Width of screen in bytes, for blitter. */
   blitsize = (height << 6)+(width >> 4);	/* Blitsize for clearing the screen. */
   buffered = 1;				/* Set screen buffer. */
   	        
   OwnBlitter();   				/* Do initial screen clearing. */
   WaitBlit();   	
   blitclearmem(blitsize,(APTR)dbbitmaps[0]->Planes[0]); 	
   blitclearmem(blitsize,(APTR)dbbitmaps[1]->Planes[0]); 
   DisownBlitter(); 

   return(0);
}

 /* Closes down the display module. */

void closedisplay()

{
   cleanup();
}
            
 /* Clears the screen. NOTE: The screen is always cleared in displaygraphics(). */
 
void clearscreen()

{
   OwnBlitter();
   WaitBlit();
   blitclearmem(blitsize,(APTR)dbbitmaps[buffered]->Planes[0]);
   DisownBlitter();  
}

 /* Draws a line using the blitter. */
 
void line(WORD x0, WORD y0, WORD x1, WORD y1)

{
   UBYTE lineinside = TRUE;

   OwnBlitter();
   WaitBlit();
   
   	/* 2D clipping to the screen. */
   
   if(x0 < 0) {
      if(x1 >= 0) {
          y0 = y0+(y1-y0)*(0-x0)/(x1-x0);
          x0 = 0;
      }
      else
         lineinside = FALSE;
    }
    else if(x1 < 0) {
       y1 = y0+(y1-y0)*(0-x0)/(x1-x0);
       x1 = 0;
    }
    if(x0 >= width) {
       if(x1 < width) {
          y0 = y0+(y1-y0)*(width-x0)/(x1-x0);
          x0 = width-1;
       }
       else
          lineinside = FALSE;
    }
    else if(x1 >= width) {
       y1 = y0+(y1-y0)*(width-x0)/(x1-x0);
       x1 = width-1;
    }
    if(y0 < 0) {
       if(y1 >= 0) {
          x0 = x0+(x1-x0)*(0-y0)/(y1-y0);
          y0 = 0;
       }
       else
          lineinside = FALSE;
    }
    else if(y1 < 0) {
       x1 = x0+(x1-x0)*(0-y0)/(y1-y0);
       y1 = 0;
    }
    if(y0 >= height) {
       if(y1 < height) {
          x0 = x0+(x1-x0)*(height-y0)/(y1-y0);
          y0 = height-1;
       }
       else
          lineinside = FALSE;
    }
    else if(y1 >= height) {
       x1 = x0+(x1-x0)*(height-y0)/(y1-y0);
       y1 = height-1;
    } 
	 
 	/* Blit the line if it is on the screen. */
	 
    if(lineinside==TRUE)  
       blitline(x0,y0,x1,y1,bytewidth,(APTR)dbbitmaps[buffered]->Planes[0]);
    DisownBlitter();  
}

 /* Switch bitmap displays. */

void showgraphics()
{
   screen->RastPort.BitMap = dbbitmaps[buffered];
   screen->ViewPort.RasInfo->BitMap = dbbitmaps[buffered];
   MakeScreen(screen); 
   RethinkDisplay();

   if(buffered == 0)
      buffered = 1;
   else
      buffered = 0; 
}

 /* Allocate a bitmap. */
 
struct BitMap *allocatebitmap()

{
    struct BitMap *bitmap;
    UBYTE i;
    
    if ((bitmap = (struct BitMap *)AllocMem(sizeof(struct BitMap),MEMF_CLEAR))!=NULL) { 
       InitBitMap(bitmap,depth,width,height);
       for (i=0;i<depth;i++) {
          if((bitmap->Planes[i] = (PLANEPTR)AllocRaster(width,height))==NULL)
             return(NULL);
       }
    }
    else
       return(NULL);
    return(bitmap);
}

 /* Releases a bitmap. */
 
void releasebitmap(struct BitMap *bitmap)

{
    UBYTE i;
    
       for (i=0;i<depth;i++) {
             FreeRaster(bitmap->Planes[i],width,height);
       FreeMem(bitmap,sizeof(struct BitMap));
    }
}

 /* Waits for input from intuition. */

UBYTE getinput(UBYTE style)
{
   if(getmessage())
      return((UBYTE)code);
   if(style == WAIT) {
      while(TRUE) {
	 signals = Wait(signalmask);
         if (signals & signalmask) {
	    if(getmessage())
	       return((UBYTE)code);
	 }
      }
   }
   else
      return(FALSE);
}

 /* Get an intuition message.  If there is one, only looking for keyboard input. */

UBYTE getmessage()
{
   struct IntuiMessage *message;
   
   if((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != NULL) {
      class = message->Class;
      code = message->Code;      
      ReplyMsg((struct Message *)message);
      if(class == VANILLAKEY)
	 return(TRUE);
      else
         return(FALSE);
   }
   return(FALSE);
}

 /* Pauses system until time has elapsed. */

void pause(ULONG secs,ULONG microsecs)

{
   timermsg.tr_node.io_Command = TR_ADDREQUEST;
   timermsg.tr_node.io_Message.mn_ReplyPort = treplyport;
   timermsg.tr_time.tv_secs = secs;
   timermsg.tr_time.tv_micro = microsecs;
   DoIO((struct IORequest *) &timermsg);
}  
   
 /* Close everything that has been opened. */  
        
void cleanup(void) 

{   
   if (treplyport)
      DeletePort(treplyport);
   if (!timeropen)
      CloseDevice((struct IORequest *) &timermsg);
   if (window)   
      CloseWindow(window);
   if (screen)
      CloseScreen(screen);
      
   releasebitmap(dbbitmaps[0]);
   releasebitmap(dbbitmaps[1]);
}


