//	SmoothUpdater.c

#include "EGG_Strs.h"
#include "SmoothUpdater.h"
#include "DirectBits.h"

static	short		SU_offscreen_depth		= 1;

#define SU_World_MAGIC		0xDEADF00D
#define SU_State_MAGIC		0x5aeeeea5

#define SUp_CHECKMAGIC(suw)		if (SUp_CheckMagic(suw)) return Err_MAGIC;

static Err
SUp_CheckMagic(const SU_World *suw)
{
	Err		err = Err_NONE;

	if (suw->magic != SU_World_MAGIC) {
		U_REPORT_S(0, U_Sev_PROBLEM, STR(728));
		err = Err_MAGIC;
	}
	
	return err;
}


Err			SU_NewWorld(const M_Rect *bounds,			/* >> */
						Boolean erase,					/* >> */
						SU_World *suw)					/* << */
{
	Err		err = Err_NONE;
	BM_WorldFlags		flags = BM_WorldFlags_KEEP_LOCAL;
	
	U_MEMCLR(sizeof(SU_World), suw);

	if (erase)
		flags |= BM_WorldFlags_CLEAR_PIXELS;
	
	err = BM_NewWorld(NULL, flags, (Rect*)bounds, 1, 0, NULL, &suw->bmw);
	
	if (!err) {
		suw->magic = SU_World_MAGIC;
		suw->port_rect = *bounds;
	}

	return err;
}

Err			SU_UpdateWorld(const M_Rect *bounds,		/* >> */
							SU_World *suw)				/* <> */
{
	Err		err = Err_NONE;
	
	SUp_CHECKMAGIC(suw);

	if (!M_Rect_EQUAL(*bounds, suw->port_rect)) {
		err = BM_UpdateWorld(1, bounds, NULL, 0, NULL, suw->bmw);
		
		if (!err) {
			suw->port_rect = *bounds;
		}
	}
	
	return err;
}


Err			SU_BlitWorld(BM_BlitMode mode,				/* >> */
							const SU_World *src,		/* >> */
							SU_World *dest)				/* <> */
{
	Err			err = Err_NONE;
	M_Rect		src_rect, dest_rect;
	Boolean		do_fast_blit = FALSE;
	
	SUp_CHECKMAGIC(src);
	SUp_CHECKMAGIC(dest);

	// note: note using src->port_rect because it won't be in right coord system
	src_rect = SU_World_GRAPH_PTR(src)->portRect;
	dest_rect = SU_World_GRAPH_PTR(dest)->portRect;
	
	if (mode == BM_Blit_COPY || mode == BM_Blit_XOR) {
		if (M_EQUAL_RECT(src_rect, dest_rect)) {
			do_fast_blit = TRUE;
		}
	}
	
	if (do_fast_blit) {
		long				pixels;
		register long		v, longs;
		void				*src_pv, *dest_pv;
		register long		*src_p, *dest_p;
		
		pixels = (long)(BM_World_ROWBYTES(dest->bmw)) * BM_World_HEIGHT(dest->bmw);
		
		if ((pixels & 0x3) != 0) {
			// force assertion so we find out (if still in the factory), but then do the normal blit
			U_ASSERT((pixels & 0x3) == 0);		// should never happen (rowbytes will be div by
			do_fast_blit = FALSE;				// 4 if we made it or it came from a gworld)
		} else {
			longs = pixels >> 2;
			
			BM_World_GET_BASEADDR_V(src->bmw, src_pv);		// doesn't need to be locked
			BM_World_GET_BASEADDR_V(dest->bmw, dest_pv);
			
			src_p = (long*)src_pv;
			dest_p = (long*)dest_pv;
			
			if (src_p && dest_p && longs > 0) {
				if (mode == BM_Blit_XOR) {
					do {
						if (v = *src_p++) {		/*=*/
							*dest_p++ ^= v;
						} else {
							++dest_p;
						}
					} while (--longs);
				} else {
					do {						// this won't work for longs == 0,
						*dest_p++ = *src_p++;	//	 but compiles into faster code
					} while (--longs);
				}
			} else {
				do_fast_blit = FALSE;
			}
		}
	}
	
	if (!do_fast_blit) {
		err = BM_LockPixels(BM_Lock_SHORT, src->bmw, NULL);
		if (!err) {
			err = BM_LockPixels(BM_Lock_SHORT, dest->bmw, NULL);
			
			if (!err) {
				err = BM_Blit(src->bmw, dest->bmw, &src_rect, &dest_rect, mode);
			
				BM_UnlockPixels(dest->bmw);
			}
			
			BM_UnlockPixels(src->bmw);
		}
	}
	
	return err;
}

			// allocates new and copies bits too
Err			SU_DuplicateWorld(const SU_World *src,		/* >> */
								SU_World *dest)			/* << */
{
	Err			err		= Err_NONE;

	SUp_CHECKMAGIC(src);

	err = SU_NewWorld(&src->port_rect, FALSE, dest);
	
	if (!err) {
		err = SU_BlitWorld(BM_Blit_COPY, src, dest);
		
		if (err) {
			(void)SU_DisposeWorld(dest);
		}
	}
	
	return err;
}


Err			SU_DisposeWorld(SU_World *suw)				/* <> */
{
	Err		err = Err_NONE;
	
	SUp_CHECKMAGIC(suw);

	err = BM_DisposeWorld(suw->bmw);
	suw->bmw = NULL;

	suw->magic &= 0xffff;
	
	return err;
}


			//	dest can be source1 or source2
Err			SU_XorWorlds(const SU_World *source1,			/* >> */
					const SU_World *source2,				/* >> */
					SU_World *dest)							/* <> */
{
	Err			err			= Err_NONE;
	Rect		the_rect	= source1->port_rect;
	Boolean		source1_and_source2_are_of_equal_size = EqualRect(&the_rect, &source2->port_rect);

	SUp_CHECKMAGIC(source1);
	SUp_CHECKMAGIC(source2);

	U_ASSERT(source1_and_source2_are_of_equal_size);
	if (!source1_and_source2_are_of_equal_size) {
		U_REPORT_S(0, U_Sev_PROBLEM, STR(729));
		err = BM_Err_WORLD_PURGED;
	}
	
	if (!err) {
		if (source1 == dest) {
			err = SU_BlitWorld(BM_Blit_XOR, source2, dest);
		} else if (source2 == dest) {
			err = SU_BlitWorld(BM_Blit_XOR, source1, dest);
		} else {
			Boolean		source_and_dest_are_of_equal_size = EqualRect(&the_rect, &dest->port_rect);
		
			U_ASSERT(source_and_dest_are_of_equal_size);
			if (!source_and_dest_are_of_equal_size) {
				U_REPORT(0, U_Sev_PROBLEM, (U_Fmt_S, STR(729)));
				err = BM_Err_WORLD_PURGED;
			}
			
			if (!err) err = SU_BlitWorld(BM_Blit_COPY, source2, dest);
			if (!err) err = SU_BlitWorld(BM_Blit_XOR, source1, dest);
		}
	}
	
	return err;
}



Err			SU_ConvertWorldToRegion(const SU_World *src,
							RgnHandle *returned_rgn)			/* << */
{
	Err			err = Err_NONE;
	RgnHandle	rgn = NewRgn();
	
	if (rgn == NULL) {
		err = U_CoSA_ERR_S(0, U_Sev_PROBLEM, Err_ALLOC, STR(730));
	} else {
		err = BM_LockPixels(BM_Lock_SHORT, src->bmw, NULL);
		if (!err) {
			PixMapHandle		pixH = SU_World_CGRAPH_PTR(src)->portPixMap;
			SInt8				hstate;
			
			U_ASSERT(pixH);
			if (pixH) {
				hstate = HGetState((Handle)pixH);

				err = BitMapToRegion(rgn, (BitMap *)U_LOCK_DH(pixH));
			
				HSetState((Handle)pixH, hstate);
			}
			
			if (err) {
				U_SYSERR_S(0, U_Sev_PROBLEM, err, STR(731));
			} else {
				OffsetRgn(rgn, src->port_rect.left, src->port_rect.top);
			}
			
			BM_UnlockPixels(src->bmw);
		}
	}
	
	*returned_rgn = rgn;

	return err;
}

Boolean SU_G_use_direct_bits = TRUE;

static	Boolean		S_use_regions	= TRUE;

Err			SU_InvertWorldOnscreen(const SU_World *src,				/* >> */
					Boolean hi_bit_only,							/* >> */
					GrafPtr dest_port)								/* <> */
{
	Err			err = Err_NONE;
	
	SUp_CHECKMAGIC(src);

	if (hi_bit_only && SU_G_use_direct_bits) {
		GrafPtr		savePort;
		
		GetPort(&savePort);

		err = BM_LockPixels(BM_Lock_SHORT, src->bmw, NULL);
		if (!err) {	
			PixMapHandle		pixH = SU_World_CGRAPH_PTR(src)->portPixMap;
			SInt8				hstate;
			
			U_ASSERT(pixH);
			if (pixH) {
				hstate = HGetState((Handle)pixH);

				DirectToggleCopyBits(U_LOCK_DH(pixH),
									 &SU_World_GRAPH_PTR(src)->portRect,
									 &dest_port->portRect, nil);
				
				HSetState((Handle)pixH, hstate);
			}
			
			BM_UnlockPixels(src->bmw);
		}

		SetPort(savePort);
	} else {
		if (S_use_regions) {
			RgnHandle	rgn;
			
			err = SU_ConvertWorldToRegion(src, &rgn);
			
			if (!err) {
				GrafPtr		savePort;
				
				GetPort(&savePort);
				SetPort(dest_port);
				InvertRgn(rgn);
				SetPort(savePort);
	
				DisposeRgn(rgn);
			}
		} else {
			// $$$ lock down src, and use copybits from world -> screen like this
			
			//	CopyBits((BitMap *)*(su->portPixMap), &(*thePort).portBits,
			//		&the_rect, &the_rect, srcXor, NULL);
		}
	}

	return err;
}

Err		SU_SaveGraphicsState(SU_GraphicsState *state)
{
	GetGWorld(&state->port, &state->device);
	
	state->magic = SU_State_MAGIC;
	
	return Err_NONE;
}


Err		SU_RestoreGraphicsState(const SU_GraphicsState *state)
{
	U_ASSERT(state->magic == SU_State_MAGIC);
	
	if (state->magic == SU_State_MAGIC) {
		SetGWorld(state->port, state->device);
	}

	return Err_NONE;
}


Err			SU_StartDrawingOffscreen(
					SU_World *dest,						/* <> */
					Boolean erase_dest,					/* >> */
					const M_Rect *bounds0,				/* >> */
					SU_GraphicsState *saved_state)		/* << */
{
	Err		err = Err_NONE;
	
	SUp_CHECKMAGIC(dest);

	err = SU_SaveGraphicsState(saved_state);
	
	if (!err && bounds0)
		err = SU_UpdateWorld(bounds0, dest);
	
	if (!err) {
		err = BM_LockPixels(BM_Lock_SHORT, dest->bmw, NULL);
		
		if (!err) {
			if (erase_dest)
				err = BM_ClearWorldToZeros(dest->bmw);
			
			if (!err) {
				SetPort(SU_World_GRAPH_PTR(dest));
				SetGDevice(SU_World_GDEVICE(dest));
				
				SetOrigin(dest->port_rect.left, dest->port_rect.top);
			}
		}
		
		if (err)
			BM_UnlockPixels(dest->bmw);
	}
	
	return err;
}

Err			SU_StopDrawingOffscreen(
					SU_World *dest,							/* <> */
					const SU_GraphicsState *saved_state)	/* >> */
{
	Err		err = Err_NONE;
	
	SUp_CHECKMAGIC(dest);
	
	SetPort(SU_World_GRAPH_PTR(dest));
	SetOrigin(0,0);

	err = SU_RestoreGraphicsState(saved_state);
	
	BM_UnlockPixels(dest->bmw);

	return err;
}


Err
SU_EraseWorld(SU_World *dest)
{
	SUp_CHECKMAGIC(dest);

	return BM_ClearWorldToZeros(dest->bmw);
}



