/* dview.c -- double-buffering through views
 * Copyright (c) 1988, I and I Computing, and Commodore-Amiga, Inc.
 *
 * 
 * Executables based on this information may be used in software
 * for Commodore Amiga computers.  All other rights reserved.
 *
 * This information is provided "as is"; no warranties are made.
 * All use is at your own risk, and no liability or responsibility is assumed.
 */

#include "sysall.h"
#include "dview.h"

/* 1 <-> 0	*/
#define OTHER_ONE( vid ) ( 1 - (vid) )
#define FORBOTH( i )	for ( i = 0; i < 2; ++i)

/* shorthand	*/
#define ALLOC( S, flags) (AllocMem( (LONG) sizeof (struct S), (LONG) (flags)))
#define FREE( ptr, S) FreeMem( (ptr), (LONG) sizeof (struct S));

#define NUMCOLORS	(32)

/* allocates and sets up a DoubleView structure based on
 * an existing screen.  Allocates two copies of everything
 * except the screen's bitmap and bitplanes.
 * creates copper lists.
 * returns non-zero if successful
 * assumes graphics.library is open
 */
initDV( dv, screen )
struct DoubleView	*dv;
struct Screen 		*screen;
{
	struct View		*vlord;
	struct View		*v;
	struct ViewPort	*vp;
	int				vx;			/* double view index 	*/
	struct BitMap	*sbitmap;	/* screen's bitmap		*/
	struct BitMap	*bmap;

	int				depth;
	int				i;

	UWORD			colortable[ NUMCOLORS ];	/* screen's colors	*/
	int				colorcount;

	vlord = ViewAddress();

	sbitmap = &screen->BitMap;
	dv->dv_Width = sbitmap->BytesPerRow << 3;
	dv->dv_Height = sbitmap->Rows;
	depth = sbitmap->Depth;

	/* get the screen's colors	*/
	colorcount = screen->ViewPort.ColorMap->Count;
	if (colorcount > NUMCOLORS) colorcount = NUMCOLORS;
	i = colorcount;
	while ( i-- )
	{
		colortable[ i ] =  GetRGB4( screen->ViewPort.ColorMap, (LONG) i);
	}

	/*
	 * initialize stuff to make it easy to clean up
	 */
	dv->dv_Current = 0;
	dv->dv_RastPorts[ 0 ] =  dv->dv_RastPorts[ 1 ] = NULL;
	dv->dv_Views[ 0 ] =  dv->dv_Views[ 1 ] = NULL;
	dv->dv_Signal = -1;

	FORBOTH( vx )
	{
		/*
		 * init rastports	
		 */
		if (!( dv->dv_RastPorts[ vx ] = ALLOC( RastPort, 0))) goto FAIL;
		InitRastPort( dv->dv_RastPorts[ vx ] );

		/*
		 * init companion bitmap and planes	
		 */
		if ( vx == 1)
		{
			/* clone screen's bitmap	*/
			if ( !( bmap =  ALLOC( BitMap, 0))) goto FAIL;

			InitBitMap( dv->dv_RastPorts[ vx ]->BitMap = bmap,
				(LONG) depth, (LONG) dv->dv_Width, (LONG) dv->dv_Height);

			for ( i = 0; i < depth; ++i )
			{
				if (!(bmap->Planes[i] =
					AllocRaster((LONG) dv->dv_Width, (LONG) dv->dv_Height)))
				{
					goto FAIL;
				}
			}
		}
		else
		{
			/* use screen's bitmap	*/
			bmap = dv->dv_RastPorts[ vx ]->BitMap = sbitmap;
		}

		/*
		 * init views and viewports			
		 */
		if (!(v = ALLOC( View, 0 ))) goto FAIL;
		InitView( dv->dv_Views[ vx ] = v );

		v->DxOffset = vlord->DxOffset;
		v->DyOffset = vlord->DyOffset;

		if (!(v->ViewPort = ALLOC( ViewPort, 0 ))) goto FAIL;
		vp = v->ViewPort;
		InitVPort( vp );

		/*
		 * init rasinfo, colormap
		 */
		if (!(vp->RasInfo = ALLOC( RasInfo, MEMF_CLEAR))) goto FAIL;
		vp->RasInfo->BitMap = bmap;

		/* overkill number of colors	*/
		if (!(vp->ColorMap = GetColorMap( (LONG) NUMCOLORS )))
		{
			goto FAIL;
		}

		/* clone colors	*/
		LoadRGB4( vp, colortable, (LONG) colorcount);

		/* other viewport init	*/
		vp->DWidth = dv->dv_Width;
		vp->DHeight = dv->dv_Height;
		vp->Modes = screen->ViewPort.Modes & ~VP_HIDE;

		/*
		 * set up user copper list to generate an interrupt
		 * on my viewport's last line
		 */
		if (!(vp->UCopIns = createUCop( vp->DHeight - 1, vp->DWidth - 1 )))
		{
			goto FAIL;
		}

		/*
		 * create view				
		 */
		MakeVPort( v, vp );
		MrgCop( v );

	}	/* end FORBOTH */

	/*
	 * copy bitmap contents		
	 */
	syncDV( dv, 0);

	/* install video interrupt handlers	*/
	if ( (dv->dv_Signal = AllocSignal( (long) -1 )) == -1) goto FAIL;

	dv->dv_SigMask = (long) 1 << dv->dv_Signal;
	printf("SigMask: %lx\n", dv->dv_SigMask );
	installHandlers( dv->dv_Signal );

	return 1;

FAIL:
	freeDV( dv );
	return 0;
}

/* frees everything set up by initDV().  Note that it
 * frees just one set of bitmap and planes.
 * can be used to free partial set
 * WARNING: Be Sure that neither view is current in graphics
 * mind.  Call RethinkDisplay() (needn't do CloseScreen())
 * to reinstall Intuition's View
 */
freeDV( dv )
struct DoubleView	*dv;
{
	int				vx;			/* double view index 	*/
	struct View		*v;
	struct ViewPort	*vp;
	struct BitMap	*bmap;
	struct RastPort	*rp;
	int				plane;

	/* 0 and -1 are both "error" indicators: no clean up	*/
	if (dv->dv_Signal != 0 && dv->dv_Signal != -1)
	{
		removeHandlers();
		FreeSignal( (long) dv->dv_Signal );
	}

	WaitTOF();		/* be sure coppers and handlers aren't in use	*/

	FORBOTH( vx )
	{
		/* View hierarchy	*/
		if ( v = dv->dv_Views[ vx ] )
		{
			if ( vp = v->ViewPort )
			{
				if ( vp->ColorMap )
				{
					FreeColorMap( vp->ColorMap );
				}

				if ( vp->RasInfo ) FREE( vp->RasInfo, RasInfo );

				/* free the intermediate copper lists created by MakeVPort	*/
				FreeVPortCopLists( vp );

				FREE( vp, ViewPort );
			}
			/* free user copper list	*/
			freeUCop( vp->UCopIns );

			/* Free the copper lists created by MrgCop	*/
			FreeCprList( v->LOFCprList );
			FreeCprList( v->SHFCprList );

			FREE( v, View );
			dv->dv_Views[ vx ] = NULL;		/* and void	*/
		}

		/* RastPort hierarchy	*/
		if ( rp = dv->dv_RastPorts[ vx ] )
		{
			if (vx == 1)		/* only the stuff I allocated	*/
			{
				if ( bmap = rp->BitMap )
				{
					plane = bmap->Depth;
					while ( plane-- )
					{
						if ( bmap->Planes[ plane ] )
						{
							FreeRaster( bmap->Planes[plane],(LONG)dv->dv_Width,
								(LONG) dv->dv_Height);
						}
					}

					FREE( bmap, BitMap );
				}
			}

			FREE( rp, RastPort );
			dv->dv_RastPorts[ vx ] = NULL;		/* and void	*/
		}
	}
}

/* copies buffers from one view buffer 'srcvid' to other */
syncDV( dv, srcvid )
struct DoubleView	*dv;
int					srcvid;
{
	BltBitMap( dv->dv_RastPorts[ srcvid ]->BitMap, 0L, 0L,
		dv->dv_RastPorts[ OTHER_ONE( srcvid ) ]->BitMap, 0L, 0L,
		(LONG) dv->dv_Width, (LONG) dv->dv_Height, (LONG) 0xc0, (LONG) 0xff, 0L);
} 

/* install view indexed by 'vid' ,
 * initiates BOVP mechanism, returns pointer
 * to offscreen rastport
 */
struct RastPort *
selectDV( dv, vid )
struct DoubleView	*dv;
int		vid;
{
	dv->dv_Current = vid;

	LoadView( dv->dv_Views[ vid ] );
	SetSignal( 0L, dv->dv_SigMask );	/* clear signal bit	*/
	return ( dv->dv_RastPorts[ OTHER_ONE( dv->dv_Current ) ] );
}

/* switches displayed view,
 * initiates BOVP mechanism, returns pointer
 * to offscreen rastport
 */
struct RastPort *
swapDV( dv )
struct DoubleView	*dv;
{
	return ( selectDV( dv, OTHER_ONE( dv->dv_Current ) ) );
}

/* waits until it safe to render into offscreen buffer */
waitDV( dv )
struct DoubleView	*dv;
{
	Wait( dv->dv_SigMask );
	return;
}

/* returns pointer to rastport for current view */
struct RastPort *
onscreenDV( dv )
struct DoubleView	*dv;
{
	return ( dv->dv_RastPorts[ dv->dv_Current ] );
}

/* returns pointer to rastport for view currently offscreen */
struct RastPort *
offscreenDV( dv )
struct DoubleView	*dv;
{
	return ( dv->dv_RastPorts[ OTHER_ONE( dv->dv_Current ) ] );
}

dumpDV( dv )
struct DoubleView	*dv;
{
	printf("DoubleView at %lx\n", dv);
	printf("current: %d, width/height %d/%d\n",
		dv->dv_Current, dv->dv_Width, dv->dv_Height);

	printf("rastports: %lx/%lx\n", dv->dv_RastPorts[0], dv->dv_RastPorts[1]);

	printf("views: %lx/%lx\n", dv->dv_Views[0], dv->dv_Views[1]);
	printf("viewports: %lx/%lx\n",
		dv->dv_Views[0]->ViewPort, dv->dv_Views[1]->ViewPort);
	printf("rasinfos %lx/%lx\n",
		dv->dv_Views[0]->ViewPort->RasInfo, dv->dv_Views[1]->ViewPort->RasInfo);
	printf("bitmaps: %lx/%lx\n",
		dv->dv_Views[0]->ViewPort->RasInfo->BitMap,
		dv->dv_Views[1]->ViewPort->RasInfo->BitMap);

	printf("rastports: %lx/%lx\n",
		dv->dv_RastPorts[0], dv->dv_RastPorts[1]);

	printf("rpbitmaps: %lx/%lx\n", 
		dv->dv_RastPorts[0]->BitMap, dv->dv_RastPorts[1]->BitMap);

}

