/** dbf.c *******************************************************************
 *
 * Intuition integrated doubly buffered display routines.
 *
 * Written by Olaf `Olsen' Barthel, 16-Sep-1991
 *
 ***************************************************************************/

#include <graphics/displayinfo.h>
#include <intuition/intuition.h>
#include <hardware/intbits.h>
#include <exec/interrupts.h>
#include <exec/memory.h>

#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>

extern struct BitMap * __regargs	 CreateBitMap(BYTE Depth,UWORD Width,UWORD Height);
extern VOID __regargs			 DeleteBitMap(struct BitMap *BitMap);

STATIC VOID __regargs			 make_ytable(WORD width,WORD height);
STATIC LONG __saveds			 VBlankServer(VOID);
VOID					 FrameSync(ULONG jiffies);
VOID					 DeleteBuffer(VOID);
BYTE					 CreateBuffer(VOID);
VOID					 SwapBits(VOID);
VOID					 CopyBits(VOID);
VOID					 LoadPalette(UWORD *Palette);

	WORD				*ytable;
	struct BitMap			*DrawBitMap;
STATIC	struct BitMap			*BitMap1,*BitMap2,*OtherBitMap,*OrigBitMap;
STATIC	WORD				 table[500];
STATIC	struct Interrupt		*VBlank;
STATIC	ULONG				 Skip;
STATIC	BYTE				 SigBit = -1;
STATIC	BYTE				 Colours;
STATIC	UWORD				 OrigPalette[32];

extern	struct Screen			*Screen;
extern	struct Process			*ThisProcess;

STATIC VOID __regargs
make_ytable(WORD width,WORD height)
{
	WORD linebytes,*pt,acc;

	linebytes	= (((width + 15) >> 4) << 1);

	ytable		= &table[0];
	pt		= ytable;
	acc		= 0;

	while(--height >= 0)
	{
		*pt++	= acc;
		acc	+= linebytes;
	}
}

STATIC LONG __saveds
VBlankServer()
{
	STATIC BYTE Sentinel = FALSE;

	if(Sentinel && !Skip)
	{
		Signal(ThisProcess,(1 << SigBit));

		Sentinel = FALSE;
	}

	if(Skip)
	{
		Sentinel = TRUE;

		Skip--;
	}

	return(0);
}

VOID
FrameSetup(ULONG jiffies)
{
	SetSignal(0,1 << SigBit);

	Skip = jiffies;
}

VOID
FrameSync()
{
	Wait(1 << SigBit);
}

VOID
DeleteBuffer()
{
	if(VBlank)
	{
		RemIntServer(INTB_VERTB,VBlank);

		FreeMem(VBlank,sizeof(struct Interrupt));

		VBlank = NULL;
	}

	if(SigBit != -1)
	{
		FreeSignal(SigBit);

		SigBit = -1;
	}

	if(OrigBitMap)
	{
		UWORD Table[32];

		memset(Table,0,sizeof(Table));

		LoadRGB4(&Screen -> ViewPort,Table,Colours);

		Screen -> ViewPort . RasInfo -> BitMap = OrigBitMap;

		MakeScreen(Screen);
		RethinkDisplay();

		LoadRGB4(&Screen -> ViewPort,OrigPalette,Colours);

		OrigBitMap = NULL;
	}

	if(BitMap1)
	{
		DeleteBitMap(BitMap1);

		BitMap1 = NULL;
	}

	if(BitMap2)
	{
		DeleteBitMap(BitMap2);

		BitMap2 = NULL;
	}
}

BYTE
CreateBuffer()
{
	WORD i;

	if((Colours = (1 << Screen -> RastPort . BitMap -> Depth)) > 32)
		Colours = 32;

	for(i = 0 ; i < Colours ; i++)
		OrigPalette[i] = GetRGB4(Screen -> ViewPort. ColorMap,i);

	if(!(BitMap1 = CreateBitMap(Screen -> RastPort . BitMap -> Depth,Screen -> Width,Screen -> Height)))
		return(FALSE);

	if(!(BitMap2 = CreateBitMap(Screen -> RastPort . BitMap -> Depth,Screen -> Width,Screen -> Height)))
	{
		DeleteBuffer();

		return(FALSE);
	}

	DrawBitMap	= BitMap1;
	OtherBitMap	= BitMap2;

	make_ytable(Screen -> Width,Screen -> Height);

	if((SigBit = AllocSignal(-1)) == -1)
	{
		DeleteBuffer();

		return(FALSE);
	}

	if(!(VBlank = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC|MEMF_CLEAR)))
	{
		DeleteBuffer();

		return(FALSE);
	}

	VBlank -> is_Node . ln_Name	= "FrameSync Interrupt";
	VBlank -> is_Node . ln_Type	= NT_INTERRUPT;
	VBlank -> is_Code		= (APTR)VBlankServer;

	Skip = 0;

	AddIntServer(INTB_VERTB,VBlank);

	OrigBitMap = Screen -> ViewPort . RasInfo -> BitMap;

	return(TRUE);
}

VOID
SwapBits()
{
	struct BitMap	*Swap;

	Screen -> ViewPort . RasInfo -> BitMap = DrawBitMap;

	MakeScreen(Screen);
	RethinkDisplay();

	Swap		= OtherBitMap;
	OtherBitMap	= DrawBitMap;
	DrawBitMap	= Swap;
}

VOID
CopyBits()
{
	CopyMem(OtherBitMap -> Planes[0],DrawBitMap -> Planes[0],OtherBitMap -> BytesPerRow * OtherBitMap -> Rows * OtherBitMap -> Depth);
}

VOID
LoadPalette(UWORD *Palette)
{
	LoadRGB4(&Screen -> ViewPort,Palette,Colours);
}
