//	COffscreen.cpp

#include "Carbon68kGlue.h"
#include "IC_Errors.h"
#include "MemUtils.h"
#include "COffscreen.h"

COffscreen	*GetNewCOffscreen(
	GrafPtr			port, 
	short			maxDepth, 
	OffscreenCB		offscreenCB, 
	void			*cbRefcon
) {
	COffscreen	*coffscreen = new(COffscreen);
	
	if (coffscreen)
		coffscreen->IOffscreen(port, maxDepth, offscreenCB, cbRefcon);
	
	return coffscreen;
}

COffscreen	*GetNewCOffscreenFromPictFile(
	FSSpec			*pictFileP, 
	Rect			*origR, 
	GrafPtr			port, 
	short			maxDepth, 
	OffscreenCB		offscreenCB, 
	void			*cbRefcon
) {
	COffscreen	*coffscreen = new(COffscreen);
	
	SetRect(origR, 0, 0, 10, 10);
	
	if (coffscreen) {
		coffscreen->IOffscreen(port, maxDepth, offscreenCB, cbRefcon);
	}
	
	if (coffscreen) {
		OSErr			err = noErr;
		short			fileRef;
		PicHandle		picH	= NULL;
		
		err = FSpOpenDF(pictFileP, fsCurPerm, &fileRef);
		if (!err) {
			OSErr		err2;
			long		eofL;
			
			err = GetEOF(fileRef, &eofL);	eofL -= 512;
			
			if (eofL < 0) err = 1;
			if (!err) err = SetFPos(fileRef, fsFromStart, 512);
			if (!err) {
				picH = (PicHandle)TrackNewHandle("pic buf", eofL);
				
				if (picH == NULL) {
					err = 1;
				} else {
					HLock((Handle)picH);
				}
			}
			
			if (!err) err = FSRead(fileRef, &eofL, *picH);
			
			err2 = FSClose(fileRef);
			if (!err) err = err2;
		}
		
		if (err) ReportErrorStr(err, "Error loading PICT file");

		if (picH) {
			*origR = (**picH).picFrame;
			
			coffscreen->Use();
			DrawPicture(picH, origR);
			coffscreen->StopUsing();

			TrackDisposeHandle((Handle)picH);
		} else {
			coffscreen->Dispose();
			coffscreen = NULL;
		}
	}

	return coffscreen;
}

void	COffscreen::IOffscreen(
	GrafPtr			port, 
	short			maxDepth, 
	OffscreenCB		offscreenCB, 
	void			*cbRefcon
) {
	i_dest				= port;
	i_maxDepth			= maxDepth;
	i_offscreenCB		= offscreenCB;
	i_cbRefcon			= cbRefcon;
	i_destIsOffscreen	= FALSE;
	i_saved_prevB		= FALSE;
	
	MakeOffscreen();
	i_invalid			= TRUE;
}

void	COffscreen::Dispose(void)
{
	StopUsing();
	
	if (i_offscreen) {
		DisposeOffscreen(i_offscreen);
		i_offscreen = NULL;
	}
	
	delete this;
}

void	COffscreen::SetMaxDepth(short maxDepth)
{
	if (maxDepth != i_maxDepth) {
		i_maxDepth	= maxDepth;
		Callback(OffscreenCB_CAUSE_UPDATE, NULL);
	}
}

Rect	COffscreen::GetFrame(void)
{
	Rect	theRect;
	
	Callback(OffscreenCB_GET_FRAME, &theRect);
	return theRect;
}

void	COffscreen::Use(void)
{
	if (!i_offscreen) {
		MakeOffscreen();
		i_invalid	= TRUE;
	}
	
	if (!i_using && i_offscreen) {
		GWorldPtr		gw = i_offscreen;
		Rect			theRect, pixRect, gwPortRect;
		PortAndDevice	savePD;
		short			depth = GetDepth();
		PixMapHandle	pm = GetPortPixMap(gw);

		i_using		= FALSE;
		
		theRect = GetFrame();
		OffsetRect(&theRect, -theRect.left, -theRect.top);
		
		GetPortBounds(gw, &gwPortRect);
		pixRect = gwPortRect;
		OffsetRect(&pixRect, -pixRect.left, -pixRect.top);
		
		if (
			!EqualRect(&theRect, &pixRect)
			|| depth != (**pm).pixelSize
		) {
			long			result;
	
			i_invalid	= TRUE;
			OffsetRect(&theRect, gwPortRect.left, gwPortRect.top);
			result		= UpdateGWorld(&gw, depth, &theRect, NULL, NULL, 0);
			
			if (result & gwFlagErr) {
				gw = NULL;
			}
		
			i_offscreen	= gw;
		} else {
			theRect = gwPortRect;
		}
		
		if (i_offscreen) {
			i_using = DrawOffscreen(i_offscreen, &savePD, &i_pixState);
			i_savePD = savePD;
			
			if (i_using) {
				ClipRect(&theRect);
			}
		}
	}
}

void	COffscreen::StopUsing(void)
{
	if (i_using) {
		PortAndDevice	savePD = i_savePD;

		i_using		= FALSE;
		i_invalid	= FALSE;
		StopDrawingOffscreen(i_offscreen, &savePD, &i_pixState);
	}
}

void	COffscreen::Blit(void)
{
	Rect			portRect;

	GetPortBounds(i_offscreen, &portRect);
	BlitRect(&portRect);
}

void	COffscreen::BlitRect(Rect *portRect)
{
	if (i_offscreen && i_dest) {
		BitMap	*source, *dest;
		
		source	= GetPix((GrafPtr)i_offscreen);
		dest	= GetPix(i_dest);
	
		if (source && dest)	{
			PortAndDevice	savePD;
			
			GetPortAndDevice(&savePD);

			SetGWorld((CGrafPtr)i_dest, NULL);
			ForeColor(blackColor);
			BackColor(whiteColor);
	
			CopyBits(
				source, 
				dest, 
				portRect, 
				portRect, 
				ditherCopy, NULL);
	
			SetPortAndDevice(&savePD);
		}

		ReleasePix((GrafPtr)i_offscreen);
		ReleasePix(i_dest);
	}
}

void	COffscreen::StretchBlit(Rect *dstR)
{
	if (i_offscreen && i_dest) {
		BitMap		*source, *dest;
		Rect		srcR;

		GetPortBounds(i_offscreen, &srcR);
		
		source	= GetPix((GrafPtr)i_offscreen);
		dest	= GetPix(i_dest);
	
		if (source && dest)	{
			PortAndDevice	savePD;
			
			GetPortAndDevice(&savePD);

			SetGWorld((CGrafPtr)i_dest, NULL);
			ForeColor(blackColor);
			BackColor(whiteColor);
	
			CopyBits(
				source, 
				dest, 
				&srcR, 
				dstR, 
				ditherCopy, NULL);
	
			SetPortAndDevice(&savePD);
		}

		ReleasePix((GrafPtr)i_offscreen);
		ReleasePix(i_dest);
	}
}

/*********************************************************
	
	private
	
*********************************************************/

void	COffscreen::Callback(
	OffscreenCBType cbType, 
	void			*cbData
) {
	(*i_offscreenCB)(cbType, (OffscreenCBData *)cbData, i_cbRefcon);
}

void	COffscreen::MakeOffscreen(void)
{
	GWorldPtr	gw;
	Rect		theRect;
	long		result;
	
	i_using = FALSE;
	
	theRect = GetFrame();
	result = NewGWorld(&gw, GetDepth(), &theRect, NULL, NULL, 0);

	if (result & gwFlagErr) {
		gw = NULL;
	}

	i_offscreen	= gw;
}

short	COffscreen::GetDepth(void)
{
	short	depth;
	
	Callback(OffscreenCB_GET_FRAME_DEPTH, &depth);
	
	if (depth > i_maxDepth) {
		depth = i_maxDepth;
	}
	
	return depth;
}


//	this assumes that I can't lock on screen gworlds
BitMap	*COffscreen::GetPix(GrafPtr pix)
{
	const BitMap		*bmp = NULL;
	
	ASSERT(i_dest);
	
	if (pix == i_dest && !i_destIsOffscreen) {
		bmp = GetPortBitMapForCopyBits((CGrafPtr)pix);
	} else {
		PixMapHandle	pm = GetGWorldPixMap((GWorldPtr)pix);

		if (LockPixels(pm)) {
			bmp = GetPortBitMapForCopyBits((CGrafPtr)pix);
		}
	}

	return (BitMap *)bmp;
}

void	COffscreen::ReleasePix(GrafPtr pix)
{
	ASSERT(i_dest);

	if (pix != i_dest || !i_destIsOffscreen) {
		PixMapHandle	pm		= GetGWorldPixMap((GWorldPtr)pix);
		GWorldFlags		flags	= GetPixelsState(pm);
	
		if (flags & pixelsLocked) {
			UnlockPixels(pm);
		}
	}
}

void	COffscreen::RestoreDest(void)
{
	ASSERT(i_saved_prevB);
	
	if (i_saved_prevB) {
		i_saved_prevB			= FALSE;
		i_destIsOffscreen		= i_prev_dest_is_offs;
		i_dest					= i_prev_dest;
	}
}

void	COffscreen::SetDest(COffscreen *offsP)
{
	ASSERT(i_saved_prevB == FALSE);
	
	if (!i_saved_prevB) {
		i_prev_dest				= i_dest;
		i_prev_dest_is_offs		= i_destIsOffscreen;
		
		i_saved_prevB			= TRUE;
		
		UpdateDest((GrafPtr)offsP->i_offscreen, TRUE);
	}
}

void	COffscreen::UpdateDest(GrafPtr destP, Boolean isOffsB)
{
	i_dest					= destP;
	i_destIsOffscreen		= isOffsB;
}
