//	Zinc Application Framework - Z_REGION.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_dsp.hpp>
#include <z_pos.hpp>
#include <z_region.hpp>
#define ZAF_REGION_INFO
#include "gbl_def.cpp"

// ----- ZafPositionStruct --------------------------------------------------

void ZafPositionStruct::ConvertCoordinates(ZafCoordinateType newType, ZafDisplay *display)
{
	// Check for a valid conversion.
	if (newType != coordinateType)
	{
		if (!display)
			display = zafDisplay;

		column = (int)display->ConvertXValue(column, coordinateType, newType);
		line = (int)display->ConvertYValue(line, coordinateType, newType);

		// Reset the new type.
		coordinateType = newType;
	}
}

ZafPositionStruct ZafPositionStruct::Position(ZafCoordinateType type) const
{
	// Convert the position.
	ZafPositionStruct position = *this;
	position.ConvertCoordinates(type);
	return (position);
}

bool ZafPositionStruct::operator==(const ZafPositionStruct &position) const
{
	// Convert the position to this coordinate system before comparing.
	ZafPositionStruct converted = position.Position(coordinateType);
	return (converted.column == column && converted.line == line);
}

bool ZafPositionStruct::operator!=(const ZafPositionStruct &position) const
{
	// Convert the position to this coordinate system before comparing.
	ZafPositionStruct converted = position.Position(coordinateType);
	return (converted.column != column || converted.line != line);
}

// ----- ZafRegionStruct ----------------------------------------------------

void ZafRegionStruct::ConvertCoordinates(ZafCoordinateType newType, ZafDisplay *display)
{
	// Check for a valid conversion.
	if (newType != coordinateType)
	{
		if (!display)
			display = zafDisplay;

		// Perform coordinate conversion.
		int width = (int)display->ConvertXValue(Width(), coordinateType, newType);
		int height = (int)display->ConvertYValue(Height(), coordinateType, newType);
		left = (int)display->ConvertXValue(left, coordinateType, newType);
		top = (int)display->ConvertYValue(top, coordinateType, newType);
		right = left + width - 1;
		bottom = top + height - 1;

		// Reset the new type.
		coordinateType = newType;
	}
}

bool ZafRegionStruct::Encompassed(const ZafRegionStruct &region) const
{
	// Convert the region to this coordinate system before comparing.
	ZafRegionStruct converted = region.Region(coordinateType);
	return (left >= converted.left && top >= converted.top && right <= converted.right && bottom <= converted.bottom);
}

bool ZafRegionStruct::Overlap(const ZafRegionStruct &region) const
{
	// Convert the region to this coordinate system before comparing.
	ZafRegionStruct converted = region.Region(coordinateType);
	return (((converted.left > left) ? converted.left : left) <= ((converted.right < right) ? converted.right : right) &&
		((converted.top > top) ? converted.top : top) <= ((converted.bottom < bottom) ? converted.bottom : bottom));
}

bool ZafRegionStruct::Overlap(const ZafPositionStruct &position) const
{
	// Convert the position to this coordinate system before comparing.
	ZafPositionStruct converted = position.Position(coordinateType);
	return (converted.column >= left && converted.column <= right &&
		converted.line >= top && converted.line <= bottom);
}

bool ZafRegionStruct::Overlap(const ZafPositionStruct &position, int columnShift, int lineShift) const
{
	// Convert the position to this coordinate system before comparing.
	ZafPositionStruct converted = position.Position(coordinateType);
	return (converted.column + columnShift >= left && converted.column + columnShift <= right &&
		converted.line + lineShift >= top && converted.line + lineShift <= bottom);
}

bool ZafRegionStruct::Overlap(const ZafRegionStruct &region, ZafRegionStruct &result) const
{
	// Convert the region to this coordinate system before comparing.
	ZafRegionStruct converted = region.Region(coordinateType);
	result.coordinateType = coordinateType;
	result.left = (left > converted.left) ? left : converted.left;
	result.top = (top > converted.top) ? top : converted.top;
	result.right = (right < converted.right) ? right : converted.right;
	result.bottom = (bottom < converted.bottom) ? bottom : converted.bottom;
	return (result.left <= result.right && result.top <= result.bottom);
}

ZafRegionStruct ZafRegionStruct::Region(ZafCoordinateType type) const
{
	// Convert the region.
	ZafRegionStruct region = *this;
	region.ConvertCoordinates(type);
	return (region);
}

bool ZafRegionStruct::Touching(const ZafPositionStruct &position) const
{
	// Convert the position to this coordinate system before comparing.
	ZafPositionStruct converted = position.Position(coordinateType);
	return (((converted.column == left || converted.column == right) &&
		converted.line >= top && converted.line <= bottom) ||
		((converted.line == top || converted.line == bottom) &&
		converted.column >= left && converted.column <= right));
}

ZafRegionStruct ZafRegionStruct::operator++(void)
{
	--left, --top, ++right, ++bottom;
	return (*this);
}

ZafRegionStruct ZafRegionStruct::operator++(int)
{
	ZafRegionStruct tmp = *this;
	left--, top--, right++, bottom++;
	return (tmp);
}

ZafRegionStruct ZafRegionStruct::operator--(void)
{
	left++, top++, right--, bottom--;
	return (*this);
}

ZafRegionStruct ZafRegionStruct::operator--(int)
{
	ZafRegionStruct tmp = *this;
	++left, ++top, --right, --bottom;
	return (tmp);
}

bool ZafRegionStruct::operator==(const ZafRegionStruct &region) const
{
	// Convert the region to this coordinate system before comparing.
	ZafRegionStruct converted = region.Region(coordinateType);
	return (converted.left == left && converted.top == top && converted.right == right && converted.bottom == bottom);
}

bool ZafRegionStruct::operator!=(const ZafRegionStruct &region) const
{
	// Convert the region to this coordinate system before comparing.
	ZafRegionStruct converted = region.Region(coordinateType);
	return (converted.left != left || converted.top != top || converted.right != right || converted.bottom != bottom);
}

// ----- ZafRegionElement & ZafRegionList -----------------------------------

ZafRegionElement::ZafRegionElement(OSWindowID _screenID, const ZafRegionStruct &_region) :
	screenID(_screenID)
{
	region = _region;
}

ZafRegionElement::ZafRegionElement(OSWindowID _screenID, int _left,
	int _top, int _right, int _bottom, ZafCoordinateType type) :
	screenID(_screenID)
{
	region.left = _left;
	region.top = _top;
	region.right = _right;
	region.bottom = _bottom;
	region.coordinateType = type;
}

ZafRegionElement::~ZafRegionElement(void)
{
}

void ZafRegionElement::Assign(OSWindowID tScreenID, int left, int top, int right,
	int bottom, ZafCoordinateType type)
{
	screenID = tScreenID;
	region.left = left;
	region.top = top;
	region.right = right;
	region.bottom = bottom;
	region.coordinateType = type;
}
  
ZafRegionList::ZafRegionList(void)
{
}

ZafRegionList::~ZafRegionList(void)
{
	ZafList::Destroy();
}


ZafRegionElement *ZafRegionList::Allocate(OSWindowID tScreenID, int left, int top, int right,
	int bottom, ZafCoordinateType type)
{
	ZafRegionElement *element = 0;
	if (freeList.First())
	{
		element = (ZafRegionElement *)freeList.First();
		freeList.Subtract(element);
		element->Assign(tScreenID, left, top, right, bottom, type);
	}
	else
		element = new ZafRegionElement(tScreenID, left, top, right, bottom, type);

	ZafList::Add(element, ZAF_NULLP(ZafElement));
	
	return (element);
}

ZafRegionElement *ZafRegionList::Allocate(OSWindowID tScreenID, ZafRegionStruct &tRegion)
{
	return (Allocate(tScreenID, tRegion.left, tRegion.top, tRegion.right, 
		tRegion.bottom, tRegion.coordinateType));
}

void ZafRegionList::Deallocate(ZafElement *element)
{
	ZafList::Subtract(element);
	freeList.Add(element);
}

void ZafRegionList::Destroy(void)
{
	while (first)
		Deallocate(first);
}

void ZafRegionList::Split(OSWindowID screenID, const ZafRegionStruct &region,
	int allocateBelow)
{
	ZafRegionStruct tRegion, sRegion;
	ZafRegionElement *dRegion;
	ZafRegionElement *t_dRegion = ZAF_NULLP(ZafRegionElement);

	// Split any overlapping regions.
	for (dRegion = First(); dRegion; dRegion = t_dRegion)
	{
		// Preset the previous region element.
		t_dRegion = dRegion->Next();

		// Object encompasses the whole region.
		if (dRegion->region.Encompassed(region))
			Deallocate(dRegion);

		// Object overlaps the region.
		else if (dRegion->region.Overlap(region, tRegion))
		{
			screenID = dRegion->screenID;
			tRegion = dRegion->region;
			sRegion = region;

			// Region is split at a maximum shown by the following set
			// of regions:
			//		 _________
			//		|_________|		1
			//		|__|___|__|		2,3,4
			//		|_________|		5
			//

			// Check for a top region (region 1 above).
			if (region.top > tRegion.top)
				Allocate(screenID, tRegion.left, tRegion.top, 
					tRegion.right, region.top - 1, region.coordinateType);
			else
				sRegion.top = tRegion.top;

			// Check for a bottom region (region 5 above).
			if (region.bottom < tRegion.bottom)
				Allocate(screenID, tRegion.left,
					region.bottom + 1, tRegion.right, tRegion.bottom,
					region.coordinateType);
			else
				sRegion.bottom = tRegion.bottom;

			// Check for a left region (region 2 above).
			if (region.left > tRegion.left)
				Allocate(screenID, tRegion.left,
					sRegion.top, region.left - 1, sRegion.bottom,
					region.coordinateType);

			// Check for a right region (region 4 above).
			if (region.right < tRegion.right)
				Allocate(screenID, region.right + 1,
					sRegion.top, tRegion.right, sRegion.bottom,
					region.coordinateType);

			// Region 3 is the object's region.
			Deallocate(dRegion);
		}
	}

	// Check for new allocation space.
	if (!First() && allocateBelow)
		Allocate(screenID, region.left, region.bottom + 100, region.right, 
			region.bottom + 120, region.coordinateType);
}

// ----- ZafUpdateRegionElement ---------------------------------------------

ZafUpdateRegionElement::ZafUpdateRegionElement(ZafWindowObject *tObject,
	ZafRegionStruct &region): object(tObject)
{
 	AddRegion(region);
}

void ZafUpdateRegionElement::AddRegion(ZafRegionStruct &region)
{
	for (ZafRegionElement *element = list.First(); element;
		element = element->Next())
	{
		if (region.Encompassed(element->region))
			return;
		else if (element->region.Encompassed(region))
		{
//			list.Subtract(element);
//			delete element;
//			list.Add(new ZafRegionElement(0, region));
			element->region = region;
			return;
		}
	}
	list.Add(new ZafRegionElement(0, region));
}

ZafWindowObject *ZafUpdateRegionElement::Object(void)
{
	return (object);
}

ZafRegionList &ZafUpdateRegionElement::List(void)
{
	return (list);
}


ZafUpdateRegionElement *ZafUpdateRegionElement::Next(void)
{
	return ((ZafUpdateRegionElement *)next);
}

// ----- ZafUpdateRegionList ------------------------------------------------
ZafUpdateRegionList::ZafUpdateRegionList(void)
{
}

ZafUpdateRegionList::~ZafUpdateRegionList(void)
{
}

void ZafUpdateRegionList::Add(ZafUpdateRegionElement *element)
{
	list.Add(element);
}

void ZafUpdateRegionList::Subtract(ZafUpdateRegionElement *element)
{
	list.Subtract(element);
}

void ZafUpdateRegionList::Destroy(void)
{
	list.Destroy();
}

ZafUpdateRegionElement *ZafUpdateRegionList::First(void)
{
	return ((ZafUpdateRegionElement *)list.First());
}
