/*
**	$Id: GfxDisplay.cpp 1.7 1995/12/03 12:16:23 olsen Exp olsen $
**
**	:ts=4
*/

/*
 * Amiga changes copyright © 1995 by Olaf Barthel, All Rights Reserved
 *
 * Copyright (c) 1992, 1993 Arno Augustin, Frank Hoering, University of
 * Erlangen-Nuremberg, Germany.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      Erlangen-Nuremberg, Germany.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software has not been validated by the ``Bundesamt fuer Zulassungen in
 * der Telekommunikation'' of the ``Deutsche Bundepost Telekom'' and thus
 * must not be used for accessing the BTX-Network of the Telekom in Germany.
 *
 * Diese Software hat keine Zulassung durch das Bundesamt fuer Zulassungen in
 * der Telekommunikation der Deutschen Bundespost Telekom und darf daher nicht
 * am Netz der Deutschen Bundespost Telekom in Deutschland betrieben werden.
 */

/****************************************************************************/

#include <graphics/modeid.h>
#include <hardware/blit.h>
#include <exec/execbase.h>

#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/keymap_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#ifdef __SASC
#include <pragmas/intuition_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/keymap_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase		*GfxBase;
extern struct ExecBase		*SysBase;
extern struct DosLibrary	*DOSBase;
extern struct Library		*KeymapBase;
extern struct Library		*UtilityBase;
#endif	// __SASC

#include <string.h>
#include <stdio.h>

/******************************************************************************/

#ifndef _GFXDISPLAY_HPP
#include "GfxDisplay.hpp"
#endif

#ifndef _ATTRIBUTES_H
#include "Attributes.h"
#endif

#ifndef _RAWKEYS_H
#include "RawKeys.h"
#endif

/****************************************************************************/

#define SPREAD(v) ((((ULONG)v) << 24) | (((ULONG)v) << 16) | (((ULONG)v) << 8) | ((ULONG)v))

GfxDisplay::GfxDisplay()
{
	LONG i;

	BtxScreen = NULL;
	BtxWindow = NULL;

	for(i = 0 ; i < 60 ; i++)
		Pen[i] = -1;

	WaitingChar = -1;

	memset(&LocalBitMap,0,sizeof(LocalBitMap));
	memset(btx_font,0,sizeof(btx_font));
	memset(fullrow_bg,0,sizeof(fullrow_bg));
	cur_fg = -1;
	cur_bg = -1;
}

GfxDisplay::~GfxDisplay()
{
	Close();
}

VOID GfxDisplay::Close(VOID)
{
	free_DRCS();
	free_font_pixmaps();

	FreeVec(LocalBitMap.Planes[0]);

	if(BtxWindow)
	{
		EraseRect(RPort,BtxWindow->BorderLeft,BtxWindow->BorderTop,BtxWindow->Width - (BtxWindow->BorderRight+1),BtxWindow->Height - (BtxWindow->BorderBottom+1));

		ReleaseColours();

		CloseWindow(BtxWindow);

		BtxWindow = NULL;
	}

	ReleaseColours();

	if(BtxScreen)
	{
		CloseScreen(BtxScreen);

		BtxScreen = NULL;
	}
}

VOID GfxDisplay::MonitorData(int *btx_rows,int *btx_fontheight)
{
	rows		= btx_rows;
	fontheight	= btx_fontheight;
}

LONG GfxDisplay::Open(STRPTR PubScreenName,int XScale,int YScale,BOOL Direct)
{
	struct Screen *PubScreen;

	DirectRender = Direct;

	if(XScale < 1)
		XScale = 1;

	if(YScale < 1)
		YScale = 1;

	if(XScale > MAX_ZOOM)
		XScale = MAX_ZOOM;

	if(XScale > MAX_ZOOM)
		XScale = MAX_ZOOM;

	if(PubScreen = LockPubScreen((UBYTE *)PubScreenName))
	{
		VPort = &PubScreen -> ViewPort;

		if(InitColours())
		{
			for(;;)
			{
				if(BtxWindow = OpenWindowTags(NULL,
					WA_PubScreen,		PubScreen,
					WA_InnerWidth,		40 * FONT_WIDTH		* XScale,
					WA_InnerHeight,		20 * FONT_HEIGHT	* YScale,
					WA_RMBTrap, 		TRUE,
					WA_IDCMP,			IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY | IDCMP_CLOSEWINDOW,
					WA_DepthGadget,		TRUE,
					WA_CloseGadget,		TRUE,
					WA_DragBar,			TRUE,
					WA_Title,			"XBTX Amiga",
					WA_AutoAdjust,		FALSE,
				TAG_DONE))
					break;
				else
				{
					if(YScale > 1)
					{
						YScale = (YScale + 1) / 2;
						continue;
					}

					if(XScale > 1)
					{
						XScale = (XScale + 1) / 2;
						continue;
					}

					ReleaseColours();
					break;
				}
			}
		}

		UnlockPubScreen(NULL,PubScreen);
	}

	if(!BtxWindow)
	{
		UWORD Pens = (UWORD)~0;
		ULONG DisplayID;

		DisplayID = FindBestModeID(
			BIDTAG_NominalWidth,	40 * FONT_WIDTH		* XScale,
			BIDTAG_NominalHeight,	20 * FONT_HEIGHT	* YScale,
			BIDTAG_Depth,			8,
		TAG_DONE);

		if(DisplayID == INVALID_ID)
			return(-1);

		if(BtxScreen = OpenScreenTags(NULL,
			SA_Depth,			8,
			SA_DisplayID,		DisplayID,
			SA_Overscan,		OSCAN_TEXT,
			SA_SharePens,		TRUE,
			SA_SysFont, 		1,
			SA_Interleaved, 	TRUE,
			SA_AutoScroll,		TRUE,
			SA_Width,			40 * FONT_WIDTH		* XScale,
			SA_Height,			20 * FONT_HEIGHT	* YScale,
			SA_Pens,			&Pens,
			SA_Quiet,			TRUE,
			SA_ShowTitle,		FALSE,
			SA_Behind,			TRUE,
		TAG_DONE))
		{
			VPort = &BtxScreen->ViewPort;

			if(InitColours())
			{
				if(!(BtxWindow = OpenWindowTags(NULL,
					WA_CustomScreen,	BtxScreen,
					WA_Width,			BtxScreen->Width,
					WA_Height,			BtxScreen->Height,
					WA_Backdrop,		TRUE,
					WA_Borderless,		TRUE,
					WA_RMBTrap, 		TRUE,
					WA_IDCMP,			IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY | IDCMP_CLOSEWINDOW,
				TAG_DONE)))
				{
					CloseWindow(BtxWindow);
					BtxWindow = NULL;

					CloseScreen(BtxScreen);
					BtxScreen = NULL;
				}
			}
			else
			{
				CloseScreen(BtxScreen);
				BtxScreen = NULL;
			}
		}
	}

	if(BtxWindow)
	{
		RPort		= BtxWindow->RPort;
		WindowPort	= BtxWindow->UserPort;
		WindowMask	= 1UL << WindowPort->mp_SigBit;

		FgPen = 0;
		BgPen = 0;

		SetABPenDrMd(RPort,Pen[FgPen],Pen[BgPen],JAM2);

		DisplayWidth	= (UWORD)(40 * FONT_WIDTH	* XScale);
		DisplayHeight	= (UWORD)(20 * FONT_HEIGHT	* YScale);

		ScaleX			= XScale;
		ScaleY			= YScale;

		InitBitMap(&LocalBitMap,8,2 * FONT_WIDTH * ScaleX,2 * FONT_HEIGHT * ScaleY);
		InitRastPort(&LocalRPort);

		InitRastPort(&TempRPort);

		SetDrMd(&TempRPort,JAM1);

		if(LocalBitMap.Planes[0] = (PLANEPTR)AllocVec(LocalBitMap.Depth * LocalBitMap.BytesPerRow * LocalBitMap.Rows,MEMF_CHIP))
		{
			LONG PlaneSize = LocalBitMap.BytesPerRow * LocalBitMap.Rows;
			LONG i;

			for(i = 1 ; i < LocalBitMap.Depth ; i++)
				LocalBitMap.Planes[i] = LocalBitMap.Planes[i - 1] + PlaneSize;

			LocalRPort.BitMap = &LocalBitMap;

			init_fonts();

			ScreenToFront(BtxWindow->WScreen);
			ActivateWindow(BtxWindow);

			return(0);
		}

		ReleaseColours();

		CloseWindow(BtxWindow);
		BtxWindow = NULL;

		if(BtxScreen)
		{
			CloseScreen(BtxScreen);
			BtxScreen = NULL;
		}
	}

	return(-1);
}

VOID GfxDisplay::PutLine(STRPTR Line)
{
	if(Line)
	{
		int i,bg,len;

		bg = 32+4+PutIndex;

		SetFgPen(bg);
		Fill(0,PutIndex*(*fontheight)*ScaleY,DisplayWidth, (*fontheight)*ScaleY);

		if((len = (int)strlen(Line)) > 40)
			len = 40;

		for(i = 0 ; i < len ; i++)
			xputc(Line[i],0,i,PutIndex,0,0,0,0,7,bg);

		PutIndex = (WORD)((PutIndex + 1) % (*rows));
	}
	else
	{
		PutIndex = 0;
		xclearscreen();
	}
}

void GfxDisplay::init_fonts()
{
   static UBYTE raw_del[] = { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
				 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
				 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f };

   extern UBYTE raw_font[];

   int n, i;

   /* primary graphic, 2nd suppl. mosaic, suppl. graphic, 3rd suppl. mosaic */
   for(n=0; n<4*96; n++) {
	  btx_font[n].raw  = (UBYTE *)&raw_font[n*2*FONT_HEIGHT];
	  btx_font[n].bits = 1;
	  btx_font[n].link = NULL;
	  for(i=0; i<4; i++)  btx_font[n].map[i] = NULL;
   }

   /* link chars into '1st supplementary mosaic' (L) set */
   for(n=0; n<32; n++) {
	  btx_font[SUP1*96+n].link	  = btx_font+SUP2*96+n;
	  btx_font[SUP1*96+n+32].link = btx_font+PRIM*96+n+32;
	  btx_font[SUP1*96+n+64].link = btx_font+SUP2*96+n+64;
   }

   /* initialize the DEL character of the L set */
   btx_font[L*96+0x7f-0x20].link = NULL;
   btx_font[L*96+0x7f-0x20].raw = raw_del;
   btx_font[L*96+0x7f-0x20].bits = 1;
   for(i=0; i<4; i++)  btx_font[L*96+0x7f-0x20].map[i] = NULL;

   /* initialize DRCS font to all NULL's */
   btx_font[DRCS*96+0].link = btx_font;    /* link 'SPACE' */
   for(n=1; n<96; n++) {
	  btx_font[DRCS*96+n].bits = 0;
	  btx_font[DRCS*96+n].raw  = NULL;
	  btx_font[DRCS*96+n].link = NULL;
	  for(i=0; i<4; i++)  btx_font[DRCS*96+n].map[i] = NULL;
   }
}

void GfxDisplay::xputc(int c,int set,int x,int y,int xdouble,int ydouble,int underline,int diacrit,int fg,int bg)
{
   btxchar *ch, *dia;

   if(x<0 || y<0 || x>39 || y>(*rows)-1) return;

   ch = btx_font + set*96 + c - 0x20;

   /* folow the link pointer */
   if(ch->link)  ch = ch->link;

   /* a yet undefined DRC should be drawn - draw a SPACE */
   if(!ch->raw)  ch = btx_font;

   if(ch->bits==1) {
	  dia = diacrit ? &btx_font[SUPP*96 + diacrit - 0x20] : (btxchar *)NULL;
	  xdraw_normal_char(ch, x, y, xdouble, ydouble, underline, dia, fg, bg);
   }
   else {  /* 2/4 bits */
	  xdraw_multicolor_char(ch, x, y, xdouble, ydouble);
   }
}

void GfxDisplay::xdraw_normal_char(btxchar *ch,int x,int y,int xd,int yd,int ul,btxchar *dia,int fg,int bg)
{
   int size;

   if(fg==TRANSPARENT)	fg = 32+4+y;
   if(bg==TRANSPARENT)	bg = 32+4+y;

   if(fg != cur_fg)
   {
	   PreparePaintFg(fg);
	   PreparePaintFgBg(fg,bg);
   }
   else
   {
	  if(bg != cur_bg)
		  PreparePaintFgBg(fg,bg);
   }

   cur_fg = fg;
   cur_bg = bg;

   size = yd*2 + xd;
   /* Pixmap not available in this size, create it */
   if(!ch->map[size])
	   ch->map[size] = rawfont2bitmap(ch->raw, (xd+1),(yd+1));

   if(dia && !dia->map[size])
	   dia->map[size] = rawfont2bitmap(dia->raw, (xd+1),(yd+1));

   if(ch->map[size])
	   DrawTemplate(ch->map[size],FgBgMinterms,x*FONT_WIDTH*ScaleX, y*(*fontheight)*ScaleY,FONT_WIDTH*ScaleX*(xd+1),(*fontheight)*ScaleY*(yd+1));

   if(dia && dia->map[size])
	   DrawTemplate(dia->map[size],FgMinterms,x*FONT_WIDTH*ScaleX, y*(*fontheight)*ScaleY,FONT_WIDTH*ScaleX*(xd+1),(*fontheight)*(yd+1)*ScaleY);

   if(ul)
   {
	   SetFgPen(fg);
	   Fill(x*FONT_WIDTH*ScaleX,y*(*fontheight)*ScaleY+((*fontheight)-1)*(yd+1)*ScaleY,FONT_WIDTH*ScaleX*(xd+1),(yd+1)*ScaleY);
   }
}

void GfxDisplay::xdraw_multicolor_char(btxchar *ch,int x,int y,int xd,int yd)
{
	int size;

	size = yd*2 + xd;

	if(!ch->map[size])
		ch->map[size] = createpixmapfromfont(ch->raw, (xd+1),(yd+1), ch->bits );

	if(ch->map[size])
		DrawTemplate(ch->map[size],NULL,x*FONT_WIDTH*ScaleX, y*(*fontheight)*ScaleY,FONT_WIDTH*ScaleX*(xd+1),(*fontheight)*(yd+1)*ScaleY);
}

void GfxDisplay::xclearscreen()
{
	int y, bg;

	for(y=0; y<(*rows); y++)
	{
		bg = 32+4+y;

		SetFgPen(bg);
		Fill(0,y*(*fontheight)*ScaleY,DisplayWidth, (*fontheight)*ScaleY);
	}
}

void GfxDisplay::xcursor(int x,int y)
{
	Complement(x*FONT_WIDTH*ScaleX,y*(*fontheight)*ScaleY,FONT_WIDTH*ScaleX,(*fontheight)*ScaleY);
}

struct BitMap * GfxDisplay::rawfont2bitmap(UBYTE *src, int xzoom, int yzoom)
{
	struct BitMap *Bits;

	if(Bits = NewBitMap(1,FONT_WIDTH*ScaleX * xzoom,FONT_HEIGHT * yzoom * ScaleY))
	{
		if(src)
		{
			struct RastPort *RPort = &TempRPort;
			int 			 x,y;
			UBYTE			 pattern;

			RPort->BitMap = Bits;

			Bits->Depth = 1;
			SetRast(RPort,0);
			SetAPen(RPort,1);

			for(y = 0 ; y < FONT_HEIGHT * yzoom * ScaleY ; y++)
			{
				pattern = src[2 * (y / (yzoom*ScaleY))];

				for(x = 0 ; x < (FONT_WIDTH / 2) * (xzoom*ScaleX) ; x++)
				{
					if(pattern & (1 << (5 - (x / (xzoom*ScaleX)))))
						WritePixel(RPort,x,y);
				}

				pattern = src[2 * (y / (yzoom*ScaleY)) + 1];

				for(x = 0 ; x < (FONT_WIDTH / 2) * xzoom*ScaleX ; x++)
				{
					if(pattern & (1 << (5 - (x / (xzoom*ScaleX)))))
						WritePixel(RPort,x + (FONT_WIDTH / 2) * xzoom*ScaleX,y);
				}
			}

			Bits->Depth = 8;
		}
	}

	return(Bits);
}

struct BitMap * GfxDisplay::createpixmapfromfont(UBYTE *src,int xzoom,int yzoom,int bits)
{
	UBYTE *d = data;
	int c,i,j,k,l,x,y,byteindex;
	struct BitMap *Bits;

	if(Bits = NewBitMap(8,FONT_WIDTH*ScaleX * xzoom,FONT_HEIGHT * yzoom * ScaleY))
	{
		struct RastPort *RPort;
		LONG LastPen = -1,ThisPen;

		RPort = &TempRPort;

		RPort->BitMap = Bits;

		SetRast(RPort,0);

		if(src)
		{
			for(y=0; y<FONT_HEIGHT; y++)
			{
				byteindex = 0;

				for(l=0; l<2; l++)
				{
					for(x=5; x>=0; x--)
					{
						for(c=0, k=0; k<bits; k++)
						{
							if(*(src+k*FONT_HEIGHT*2) & (1<<x) )
								c |= 1<<k;
						}

						for(j=0; j<xzoom*ScaleX; j++)
							row[byteindex++] = (UBYTE)(bits==4 ? c+16 : c+32);
					}

					src++;
				}

				for(j=0; j<(yzoom*ScaleY); j++)
				{
					for(i=0; i<byteindex; i++)
						*d++ = row[i];
				}
			}

			d = data;

			for(y = 0 ; y < FONT_HEIGHT * yzoom * ScaleY ; y++)
			{
				for(x = 0 ; x < FONT_WIDTH*ScaleX * xzoom ; x++)
				{
					ThisPen = Pen[*d++];

					if(LastPen != ThisPen)
						SetAPen(RPort,LastPen = ThisPen);

					WritePixel(RPort,x,y);
				}
			}
		}
	}

	return(Bits);
}

void GfxDisplay::define_raw_DRC(int c,UBYTE *data, int bits)
{
	struct btxchar *ch = btx_font + DRCS*96 + c - 0x20;
	int n;

	if(ch->raw)
		delete ch->raw;

	for(n=0; n<4; n++)
	{
		if(ch->map[n])
		{
			DisposeBitMap(ch->map[n]);
			ch->map[n] = NULL;
		}
	}

	if(ch->raw = new UBYTE[2*FONT_HEIGHT*bits])
	{
		memset(ch->raw,0,2*FONT_HEIGHT*bits);

		ch->bits = (UBYTE)bits;

		for(n=0; n<2*FONT_HEIGHT*bits; n++)
			ch->raw[n] = data[n];

		ch->link = NULL;
	}
}

void GfxDisplay::free_DRCS()
{
	int i, n;

	btx_font[DRCS*96+0].link = btx_font;	/* link 'SPACE' */

	for(n=DRCS*96+1; n<(DRCS+1)*96; n++)
	{
		if(btx_font[n].raw)
		{
			delete btx_font[n].raw;
			btx_font[n].raw = NULL;
		}
		for(i=0; i<4; i++)
		{
			if(btx_font[n].map[i])
			{
				DisposeBitMap(btx_font[n].map[i]);
				btx_font[n].map[i] = NULL;
			}
			btx_font[n].link = NULL;
			btx_font[n].bits = 0;
		}
	}
}

void GfxDisplay::free_font_pixmaps()
{
	int i, n;

	for(n=0; n<6*96; n++)
	{
		if(!btx_font[n].link)
		{
			for(i=0; i<4; i++)
			{
				if(btx_font[n].map[i])
				{
					DisposeBitMap(btx_font[n].map[i]);
					btx_font[n].map[i] = NULL;
				}
			}
		}
	}
}

void GfxDisplay::default_colors()
{
	init_colormap();
	store_colors();
}

void GfxDisplay::store_colors()
{
	int n;

//	for(n = 0 ; n < 16 ; n++)
//		SetColour(n,colormap[n].red,colormap[n].green,colormap[n].blue);

	for(n = 0 ; n < 8 ; n++)
	{
		SetColour(n + 16,colormap[n + 16].red,colormap[n + 16].green,colormap[n + 16].blue);	// clut 2
		SetColour(n + 24,colormap[n + 24].red,colormap[n + 24].green,colormap[n + 24].blue);	// clut 3
	}

	for(n = 0 ; n < 4 ; n++)	// dclut
		SetColour(32 + n,colormap[32 + n].red,colormap[32 + n].green,colormap[32 + n].blue);

	for(n = 0 ; n < 24 ; n++)
		SetColour(32 + 4 + n,colormap[32 + 4 + n].red,colormap[32 + 4 + n].green,colormap[32 + 4 + n].blue);
}

void GfxDisplay::init_colormap()
{
   int n;

   /* clut 0 + clut 1 */
   for(n=0; n<16; n++) {  /* page 112) */
	  if(n==8)	{ colormap[n] = colormap[0]; continue; }
	  colormap[n].red	= (UBYTE)(((n&1)>0) ? ( ((n&8)==0) ? 0xff : 0x7f ) : 0);
	  colormap[n].green = (UBYTE)(((n&2)>0) ? ( ((n&8)==0) ? 0xff : 0x7f ) : 0);
	  colormap[n].blue	= (UBYTE)(((n&4)>0) ? ( ((n&8)==0) ? 0xff : 0x7f ) : 0);
   }

   /* clut 2 + clut 3 */
   for(n=0; n<8; n++) {
	  colormap[n+16].red   = colormap[n+24].red   = (UBYTE)(n&1 ? 0xff : 0);
	  colormap[n+16].green = colormap[n+24].green = (UBYTE)(n&2 ? 0xff : 0);
	  colormap[n+16].blue  = colormap[n+24].blue  = (UBYTE)(n&4 ? 0xff : 0);
   }

   /* DCLUT */
   for(n=0; n<4; n++) {
	  colormap[32+n].red   = colormap[n].red;
	  colormap[32+n].green = colormap[n].green;
	  colormap[32+n].blue  = colormap[n].blue;
	  dclut[n] = n;
   }

   /* fullrow background (BLACK) */
   for(n=0; n<24; n++) {
	  colormap[32+4+n].red	 = colormap[0].red;
	  colormap[32+4+n].green = colormap[0].green;
	  colormap[32+4+n].blue  = colormap[0].blue;
	  fullrow_bg[n] = 0;
   }
}

void GfxDisplay::define_color(int index,int r,int g,int b)	// 4 bit pro Eintrag
{
	int i;

	r &= 15;
	g &= 15;
	b &= 15;

	r = (r << 4) | r;
	g = (g << 4) | g;
	b = (b << 4) | b;

	colormap[index].red   = (UBYTE)r;
	colormap[index].green = (UBYTE)g;
	colormap[index].blue  = (UBYTE)b;
	SetColour(index,r,g,b);

	/* if DCLUT contains this color, change DCLUT color too */
	for(i=0; i<4; i++)
	{
		if(dclut[i]==index)
			define_DCLUT(i, index);
	}

	/* if fullrow_bg contains this color, change fullrow color too */
	for(i=0; i<24; i++)
	{
		if(fullrow_bg[i]==index)
			define_fullrow_bg(i, index);
	}
}

void GfxDisplay::define_DCLUT(int entry,int index)
{
   dclut[entry] = index;
   colormap[32+entry].red	= colormap[index].red;
   colormap[32+entry].green = colormap[index].green;
   colormap[32+entry].blue	= colormap[index].blue;

   SetColour(32 + entry,colormap[32 + entry].red,colormap[32 + entry].green,colormap[32 + entry].blue);
}

void GfxDisplay::define_fullrow_bg(int row,int index)
{
   fullrow_bg[row] = index;
   colormap[32+4+row].red	= colormap[index].red;
   colormap[32+4+row].green = colormap[index].green;
   colormap[32+4+row].blue	= colormap[index].blue;

   SetColour(32 + 4 + row,colormap[32 + 4 + row].red,colormap[32 + 4 + row].green,colormap[32 + 4 + row].blue);
}

/******************************************************************************/

#define V0H0	(0)
#define V0H1	(NANBC | ANBNC | NANBC | ANBC)
#define V1H0	(NABNC | ABNC | NABC | ABC)
#define V1H1	(NANBC | NANBNC | NABC | NABNC | ANBC | ANBNC | ABC | ABNC)

#define PAINT_a (NANBC | ANBC | NABNC | ABNC | NABC | ABC)
#define PAINT_b (ANBC | NANBC)

VOID GfxDisplay::PreparePaintFgBg(UBYTE Fore,UBYTE Back,UBYTE *Minterms)
{
	UBYTE Mask,Term,i;

	if(!Minterms)
		Minterms = FgBgMinterms;

	Fore = (UBYTE)Pen[Fore];
	Back = (UBYTE)Pen[Back];

	for(i = 0 ; i < 8 ; i++)
	{
		Mask = (UBYTE)(1 << i);

		if(Fore & Mask)
		{
			if(Back & Mask)
				Term = V1H1;
			else
				Term = V1H0;
		}
		else
		{
			if(Back & Mask)
				Term = V0H1;
			else
				Term = V0H0;
		}

		Minterms[i] = Term;
	}
}

VOID GfxDisplay::PreparePaintFg(UBYTE Fore,UBYTE *Minterms)
{
	UBYTE Mask,Term,i;

	if(!Minterms)
		Minterms = FgMinterms;

	Fore = (UBYTE)Pen[Fore];

	for(i = 0 ; i < 8 ; i++)
	{
		Mask = (UBYTE)(1 << i);

		if(Fore & Mask)
			Term = PAINT_a;
		else
			Term = PAINT_b;

		Minterms[i] = Term;
	}
}

VOID GfxDisplay::DrawTemplate(CONST struct BitMap *BitMap,CONST UBYTE *Minterms,UWORD Left,UWORD Top,UWORD Width,UWORD Height)
{
	Left	+= BtxWindow->BorderLeft;
	Top		+= BtxWindow->BorderTop;

	if(Minterms)
	{
		if(DirectRender)
		{
			LONG i;

			for(i = 0 ; i < 8 ; i++)
			{
				SetWriteMask(RPort,1 << i);
				BltBitMapRastPort((struct BitMap *)BitMap,0,0,RPort,Left,Top,Width,Height,Minterms[i]);
			}

			SetWriteMask(RPort,~0);
		}
		else
		{
			LONG i;

			SetRast(&LocalRPort,0);

			ClipBlit(RPort,Left,Top,&LocalRPort,0,0,Width,Height,0xC0);

			for(i = 0 ; i < 8 ; i++)
				BltBitMap((struct BitMap *)BitMap,0,0,&LocalBitMap,0,0,Width,Height,Minterms[i],1 << i,NULL);

			BltBitMapRastPort(&LocalBitMap,0,0,RPort,Left,Top,Width,Height,0xC0);
		}
	}
	else
		BltBitMapRastPort((struct BitMap *)BitMap,0,0,RPort,Left,Top,Width,Height,0xC0);
}

VOID GfxDisplay::DisposeBitMap(struct BitMap *Bits)
{
	if(Bits)
	{
		FreeVec(Bits->Planes[0]);

		FreeVec(Bits);
	}
}

struct BitMap *GfxDisplay::NewBitMap(LONG Depth,LONG Width,LONG Height)
{
	struct BitMap *Bits;

	if(Bits = (struct BitMap *)AllocVec(sizeof(struct BitMap),MEMF_ANY))
	{
		InitBitMap(Bits,8,Width,Height);

		if(Depth == 1)
		{
			if(Bits->Planes[0] = (PLANEPTR)AllocVec(Bits->BytesPerRow * Bits->Rows,MEMF_CHIP))
			{
				LONG i;

				for(i = 1 ; i < 8 ; i++)
					Bits->Planes[i] = Bits->Planes[0];
			}
		}
		else
		{
			if(Bits->Planes[0] = (PLANEPTR)AllocVec(Depth * Bits->BytesPerRow * Bits->Rows,MEMF_CHIP))
			{
				LONG PlaneSize = Bits->BytesPerRow * Bits->Rows;
				LONG i;

				for(i = 1 ; i < Depth ; i++)
					Bits->Planes[i] = Bits->Planes[i - 1] + PlaneSize;
			}
		}

		if(Bits->Planes[0])
			return(Bits);
		else
			FreeVec(Bits);
	}

	return(NULL);
}

VOID GfxDisplay::SetFgPen(LONG Index)
{
	if(Index != FgPen)
	{
		FgPen = Index;

		SetAPen(RPort,Pen[Index]);
	}
}

VOID GfxDisplay::SetBgPen(LONG Index)
{
	if(Index != BgPen)
	{
		BgPen = Index;

		SetBPen(RPort,Pen[Index]);
	}
}

VOID GfxDisplay::SetPens(LONG Fg,LONG Bg)
{
	if(Fg != FgPen || Bg != BgPen)
	{
		FgPen = Fg;
		BgPen = Bg;

		SetABPenDrMd(RPort,Pen[Fg],Pen[Bg],JAM2);
	}
}

VOID GfxDisplay::Fill(LONG Left,LONG Top,LONG Width,LONG Height)
{
	if(Width > 0 && Height > 0)
	{
		Left	+= BtxWindow->BorderLeft;
		Top		+= BtxWindow->BorderTop;

		RectFill(RPort,Left,Top,Left + Width - 1,Top + Height - 1);
	}
}

VOID GfxDisplay::Complement(LONG Left,LONG Top,LONG Width,LONG Height)
{
	if(Width > 0 && Height > 0)
	{
		Left	+= BtxWindow->BorderLeft;
		Top		+= BtxWindow->BorderTop;

		SetABPenDrMd(RPort,(ULONG)~0,0,JAM1 | COMPLEMENT);

		RectFill(RPort,Left,Top,Left + Width - 1,Top + Height - 1);

		SetABPenDrMd(RPort,Pen[FgPen],Pen[BgPen],JAM2);
	}
}

VOID GfxDisplay::SetColour(LONG Index,UBYTE Red,UBYTE Green,UBYTE Blue)
{
	SetRGB32(VPort,Pen[Index],SPREAD(Red),SPREAD(Green),SPREAD(Blue));
}

ULONG GfxDisplay::WaitMask(VOID)
{
	return(WindowMask);
}

LONG GfxDisplay::Waiting(VOID)
{
	if(WaitingChar == -1)
	{
		struct IntuiMessage *Message;

		while(Message = (struct IntuiMessage *)GetMsg(WindowPort))
		{
			if(Message->Class == IDCMP_RAWKEY)
			{
				if((WaitingChar = (WORD)MapKey(Message)) >= 0)
				{
					ReplyMsg((struct Message *)Message);

					return(1);
				}
			}
			else
			{
				if(Message->Class == IDCMP_CLOSEWINDOW)
				{
					ReplyMsg((struct Message *)Message);

					WaitingChar = '\033';

					return(1);
				}
			}

			ReplyMsg((struct Message *)Message);
		}

		return(0);
	}
	else
		return(1);
}

LONG GfxDisplay::GetChar(VOID)
{
	if(WaitingChar == -1)
	{
		struct IntuiMessage *Message;

		while(Message = (struct IntuiMessage *)GetMsg(WindowPort))
		{
			if(Message->Class == IDCMP_RAWKEY)
			{
				LONG Result = MapKey(Message);

				ReplyMsg((struct Message *)Message);

				return(Result);
			}
			else
			{
				if(Message->Class == IDCMP_CLOSEWINDOW)
				{
					ReplyMsg((struct Message *)Message);

					return('\033');
				}
			}

			ReplyMsg((struct Message *)Message);
		}

		return(-1);
	}
	else
	{
		LONG Result = WaitingChar;

		WaitingChar = -1;

		return(Result);
	}
}

LONG GfxDisplay::MapKey(struct IntuiMessage *Message)
{
	static LONG Table[][2] =
	{
		RAWKEY_CursorUp,	KEY_CursorUp,
		RAWKEY_CursorDown,	KEY_CursorDown,
		RAWKEY_CursorRight, KEY_CursorRight,
		RAWKEY_CursorLeft,	KEY_CursorLeft,

		RAWKEY_Help,		KEY_Help,

		RAWKEY_F1,			-1,
		RAWKEY_F2,			-1,
		RAWKEY_F3,			-1,
		RAWKEY_F4,			-1,
		RAWKEY_F5,			-1,
		RAWKEY_F6,			-1,
		RAWKEY_F7,			-1,
		RAWKEY_F8,			-1,
		RAWKEY_F9,			-1,
		RAWKEY_F10, 		-1,

		-1
	};

	UBYTE Buffer[10];
	LONG i;

	for(i = 0 ; Table[i][0] != -1 ; i++)
	{
		if((Message->Code & ~IECODE_UP_PREFIX) == Table[i][0])
		{
			if(Message->Code & IECODE_UP_PREFIX)
				return(-1);
			else
				return(Table[i][1]);
		}
	}

	Event.ie_Class		= IECLASS_RAWKEY;
	Event.ie_SubClass 	= 0;
	Event.ie_Code 		= Message->Code;
	Event.ie_Qualifier	= Message->Qualifier;
	Event.ie_EventAddress = (APTR *) *((ULONG *)Message->IAddress);

	Buffer[0] = 0;

	if(MapRawKey(&Event,(char *)&Buffer[0],10,(struct KeyMap *)NULL) > 0)
		return(Buffer[0]);
	else
		return(-1);
}

ULONG GfxDisplay::FindBestModeID(Tag FirstTag,...)
{
	struct TagItem			*List,
							*Item;
	ULONG					 Mode;

	ULONG					 BestMode;
	LONG					 BestWidth,
							 BestHeight,
							 BestDepth;

	ULONG					 MustHave,
							 MustNotHave;
	ULONG					 MonitorID;
	struct ViewPort			*ViewPort;
	ULONG					 SourceID;
	UWORD					 NominalWidth,
							 NominalHeight;
	UWORD					 DesiredWidth,
							 DesiredHeight;
	UBYTE					 Depth;

	UBYTE					 RedBits;
	UBYTE					 BlueBits;
	UBYTE					 GreenBits;

	struct DisplayInfo		 DisplayInfo;
	struct DimensionInfo	 DimensionInfo;

	LONG					 Width,
							 Height;

	MustHave		= NULL;
	MustNotHave		= SPECIAL_FLAGS;
	Depth			= 1;
	MonitorID		= INVALID_ID;
	SourceID		= INVALID_ID;
	RedBits			= 4;
	BlueBits		= 4;
	GreenBits		= 4;
	NominalWidth	= 640,
	NominalHeight	= 200;

	List = (struct TagItem *)&FirstTag;

	while(Item = NextTagItem(&List))
	{
		switch(Item->ti_Tag)
		{
			case BIDTAG_DIPFMustHave:

				MustHave = Item->ti_Data;
				break;

			case BIDTAG_DIPFMustNotHave:

				MustNotHave = Item->ti_Data;
				break;

			case BIDTAG_RedBits:

				RedBits = (UBYTE)Item->ti_Data;
				break;

			case BIDTAG_GreenBits:

				GreenBits = (UBYTE)Item->ti_Data;
				break;

			case BIDTAG_BlueBits:

				BlueBits = (UBYTE)Item->ti_Data;
				break;

			case BIDTAG_MonitorID:

				MonitorID = Item->ti_Data;
				break;

			case BIDTAG_NominalWidth:

				NominalWidth = (UWORD)Item->ti_Data;
				break;

			case BIDTAG_NominalHeight:

				NominalHeight = (UWORD)Item->ti_Data;
				break;
		}
	}

	DesiredWidth	= NominalWidth;
	DesiredHeight	= NominalHeight;

	List = (struct TagItem *)&FirstTag;

	if(ViewPort = (struct ViewPort *)GetTagData(BIDTAG_ViewPort,NULL,List))
	{
		NominalWidth	= ViewPort->DWidth;
		NominalHeight	= ViewPort->DHeight;
		DesiredWidth	= NominalWidth;
		DesiredHeight	= NominalHeight;
		Depth			= ViewPort->RasInfo->BitMap->Depth;
		SourceID		= GetVPModeID(ViewPort);
	}

	if(Item = FindTagItem(BIDTAG_SourceID,List))
	{
		if(GetDisplayInfoData((APTR)NULL,(UBYTE *)&DisplayInfo,sizeof(DisplayInfo),DTAG_DISP,SourceID))
			MustHave = (DisplayInfo.PropertyFlags & SPECIAL_FLAGS) | MustHave;
		else
			return(INVALID_ID);

		if(GetDisplayInfoData((APTR)NULL,(UBYTE *)&DimensionInfo,sizeof(DimensionInfo),DTAG_DIMS,SourceID))
		{
			NominalWidth	= (UWORD)(DimensionInfo.Nominal.MaxX - DimensionInfo.Nominal.MinX + 1);
			NominalHeight	= (UWORD)(DimensionInfo.Nominal.MaxY - DimensionInfo.Nominal.MinY + 1);
			DesiredWidth	= NominalWidth;
			DesiredHeight	= NominalHeight;
		}
		else
			return(INVALID_ID);
	}

	List = (struct TagItem *)&FirstTag;

	while(Item = NextTagItem(&List))
	{
		switch(Item->ti_Tag)
		{
			case BIDTAG_DesiredWidth:

				DesiredWidth = (UWORD)Item->ti_Data;
				break;

			case BIDTAG_DesiredHeight:

				DesiredHeight = (UWORD)Item->ti_Data;
				break;

			case BIDTAG_Depth:

				Depth = (UBYTE)Item->ti_Data;
				break;
		}
	}

	BestMode	= INVALID_ID;
	BestWidth	= 0;
	BestHeight	= 0;
	BestDepth	= 0;

	Mode = INVALID_ID;

	while((Mode = NextDisplayInfo(Mode)) != INVALID_ID)
	{
		if(MonitorID != INVALID_ID)
		{
			if((Mode & MONITOR_ID_MASK) != MonitorID)
				continue;
		}

		if(ModeNotAvailable(Mode) & ~DI_AVAIL_NOTWITHGENLOCK)
			continue;

		if(GetDisplayInfoData((APTR)NULL,(UBYTE *)&DisplayInfo,sizeof(DisplayInfo),DTAG_DISP,Mode) && GetDisplayInfoData((APTR)NULL,(UBYTE *)&DimensionInfo,sizeof(DimensionInfo),DTAG_DIMS,Mode))
		{
			if(DimensionInfo.MaxDepth < Depth || DisplayInfo.RedBits < RedBits || DisplayInfo.GreenBits < GreenBits || DisplayInfo.BlueBits < BlueBits)
				continue;

			if(DimensionInfo.MaxDepth > 8 && Depth <= 8)
				continue;


			if(MustHave)
			{
				if((DisplayInfo.PropertyFlags & MustHave) != MustHave)
					continue;
			}

			if(MustNotHave)
			{
				if(DisplayInfo.PropertyFlags & MustNotHave)
					continue;
			}

			Width	= DimensionInfo.TxtOScan.MaxX - DimensionInfo.TxtOScan.MinX + 1;
			Height	= DimensionInfo.TxtOScan.MaxY - DimensionInfo.TxtOScan.MinY + 1;

			if(DesiredWidth <= Width && DesiredHeight <= Height && NominalWidth <= DimensionInfo.Nominal.MaxX - DimensionInfo.Nominal.MinX + 1 && NominalHeight <= DimensionInfo.Nominal.MaxY - DimensionInfo.Nominal.MinY + 1)
			{
				if(!BestWidth || !BestHeight || !BestDepth)
				{
					BestMode	= Mode;
					BestDepth	= DimensionInfo.MaxDepth;
					BestWidth	= DesiredWidth;
					BestHeight	= DesiredHeight;
				}
				else
				{
					if(DimensionInfo.MaxDepth <= BestDepth && Width <= BestWidth && Height <= BestHeight)
					{
						BestMode	= Mode;
						BestDepth	= DimensionInfo.MaxDepth;
						BestWidth	= DesiredWidth;
						BestHeight	= DesiredHeight;
					}
				}
			}
		}
	}

	return(BestMode);
}

VOID GfxDisplay::ReleaseColours(VOID)
{
	LONG i;

	for(i = 0 ; i < 60 ; i++)
	{
		ReleasePen(VPort->ColorMap,Pen[i]);
		Pen[i] = -1;
	}
}

BOOL GfxDisplay::InitColours(VOID)
{
	int n;

	init_colormap();

	for(n = 0 ; n < 16 ; n++)
	{
		Pen[n] = (LONG)ObtainBestPen(VPort->ColorMap,SPREAD(colormap[n].red),SPREAD(colormap[n].green),SPREAD(colormap[n].blue),
			OBP_Precision,	PRECISION_IMAGE,
			OBP_FailIfBad,	TRUE,
		TAG_DONE);

		if(Pen[n] == -1)
		{
			ReleaseColours();
			return(FALSE);
		}
	}

	for(n = 16 ; n < 60 ; n++)
	{
		Pen[n] = (LONG)ObtainPen(VPort->ColorMap,(ULONG)-1, 0,0,0, PEN_EXCLUSIVE|PEN_NO_SETCOLOR);

		if(Pen[n] == -1)
		{
			ReleaseColours();
			return(FALSE);
		}
	}

	for(n = 0 ; n < 8 ; n++)
	{
		SetColour(n + 16,colormap[n + 16].red,colormap[n + 16].green,colormap[n + 16].blue);	// clut 2
		SetColour(n + 24,colormap[n + 24].red,colormap[n + 24].green,colormap[n + 24].blue);	// clut 3
	}

	for(n = 0 ; n < 4 ; n++)	// dclut
		SetColour(32 + n,colormap[32 + n].red,colormap[32 + n].green,colormap[32 + n].blue);

	for(n = 0 ; n < 24 ; n++)
		SetColour(32 + 4 + n,colormap[32 + 4 + n].red,colormap[32 + 4 + n].green,colormap[32 + 4 + n].blue);

	return(TRUE);
}
