#ifndef __TEST_RECTANGLE_H__
#define __TEST_RECTANGLE_H__

/*
 *	Testing for rectangle overlap
 */


struct TestPoint {
	int x;
	int y;

	TestPoint() : x(0), y(0) {}
	TestPoint(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
};

struct TestRect {
	int m_left;
	int m_top;
	int m_right;
	int m_bottom;

	TestRect()
	{
		SetEmpty();
	}

	TestRect(const TestRect & aCopy)
	{
		SetRect(aCopy);
	}

	TestRect(int left, int top, int right, int bottom)
	{
		SetRect(left,top,right,bottom);
	}

	void SetEmpty()
	{
		m_left = m_top = m_right = m_bottom;
	}

	void SetRect(const TestRect & aCopy)
	{
		SetRect(aCopy.m_left,aCopy.m_top,aCopy.m_right,aCopy.m_bottom);
	}

	void SetRect(int left, int top, int right, int bottom)
	{
		m_left   = left;
		m_top    = top;
		m_right  = right;
		m_bottom = bottom;
	}

	TestPoint TopLeft() const
	{
		return TestPoint(m_top,m_left);
	}

	TestPoint TopRight() const
	{
		return TestPoint(m_top,m_right);
	}

	TestPoint BottomLeft() const
	{
		return TestPoint(m_bottom,m_left);
	}

	TestPoint BottomRight() const
	{
		return TestPoint(m_bottom,m_right);
	}

	bool IsEqual(const TestRect & other) const
	{
		return( (other.m_left  == m_left ) && (other.m_top    == m_top   ) &&
		        (other.m_right == m_right) && (other.m_bottom == m_bottom) );
	}

	int Width() const
	{
		return m_right-m_left;
	}

	int Height() const
	{
		return m_bottom-m_top;
	}

	bool HasArea() const
	{
		return(Width() > 0 && Height() > 0);
	}

	bool PointOnRect(int x, int y) const
	{
		return( x >= m_left && x <= m_right && y >= m_top && y <= m_bottom );
	}

	bool PointInRect(TestPoint point) const
	{
		return PointInRect(point.x, point.y);
	}

	bool PointInRect(int x, int y) const
	{
		return( x > m_left && x < m_right && y > m_top && y < m_bottom );
	}

#ifdef _DEBUG
	static bool TestGetUnion()
	{
		/* Samples of content overlap we check for

		0123456789ABCD     0123456789ABC    0123456789    0123456
		1 aaaaa  aaaaa         aaaaa        aaaaaaaaaa    aaaabbb
		2 a   a  a   a         a   a        a        a    a  a  b
		3 a BBBBBBBB a         a   a        a  bbbb  a    aaaa  b
		4 a B a  a B a         a   a        a  b  b  a    b     b
		5 aaBaa  aaBaa     bbbbabbbabbbb    a  b  b  a    bbbbbbb
		6   B      B       b   a   a   b    a  bbbb  a 
		7 aaBaa  aaBaa     bbbbabbbabbbb    a        a    ababab
		8 a B a  a B a         a   a        aaaaaaaaaa    b    a
		9 a BBBBBBBB a         a   a                      ababab
		A a   a  a   a         a   a
		B aaaaa  aaaaa         aaaaa
		
		1 aaaa      aaabbbaaa
		2 a  a      a  b b  a
		3 bbbbbbb   aaababbaa
		4 b  a  b      b b
		5 bbbbbbb      bbb

		If the content does not overlap, we're not interested...

		0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789
		1 aaaaa       aaaa         aaa
		2 a   bbbbb   a  a         a a
		3 a   a   b   aaaBbbb      aaa   
		4 a   b   b      b  b       
		5 a   abbbb      bbbb       bbbbb
		6 aaaaa
		*/

		typedef struct _tagTestData {
			int aLeft;  int aTop;  int aRight; int aBottom;
			int bLeft;  int bTop;  int bRight; int bBottom;
			bool shouldPass;
		} TestData;

		TestData data[] = {
			// Connect 4
			{ 0x2,0x1,0x5,0x6, 0x4,0x3,0xb,0x9, true },
			{ 0x9,0x1,0xd,0x5, 0x4,0x3,0xb,0x9, true },
			{ 0x2,0x7,0x6,0xb, 0x4,0x3,0xb,0x9, true },
			{ 0x9,0x7,0xd,0xb, 0x4,0x3,0xb,0x9, true },
			
			// +
			{ 0x4,0x1,0x8,0xb, 0x0,0x5,0xc,0x7, true },

			// doughnut
			{ 0x0,0x0,0x8,0x9, 0x3,0x3,0x6,0x6, true },

			// same overlapping area
			{ 0,0,5,5,  0,0,5,5, true  },

			// smaller, but in the corner
			{ 0,0,3,3,  0,0,6,6, true  },
			
			// "L"
			{ 1,1,4,5,  1,3,7,5, true  },

			// "T"
			{ 1,1,9,3,  4,1,6,5, true  },

			// Side by side
			{ 1,1,6,6,  6,2,9,5, false },

			// Shared corner
			{ 0,0,3,3,  3,3,6,6, false },

			// Off to the right
			{ 0,0,2,2,  3,0,5,2, false },
			
			// Off to the bottom right
			{ 0,0,2,2,  4,4,6,6, false },

			// Off to the bottom
			{ 0,0,2,2,  0,4,2,4, false },

			// Null rect
			{ 0,0,0,0,  0,0,4,4, false },

			// Inverted rect
			{ 5,5,0,0,  0,0,5,5, false }
		};

		const int count = sizeof(data)/sizeof(data[0]);

		TestRect aBox,bBox;
		bool aResult = false, bResult = false;
		int maxX = 0, maxY = 0, x = 0, y = 0;
		enum {
			space   = 0x00,
			alpha   = 0x01,
			beta    = 0x02,
			overlap = alpha|beta
		};
		int character = space;

		for( int idx = 0; idx < count; idx++ )
		{
			// Build the rects
			aBox.SetRect( data[idx].aLeft, data[idx].aTop, data[idx].aRight, data[idx].aBottom );
			bBox.SetRect( data[idx].bLeft, data[idx].bTop, data[idx].bRight, data[idx].bBottom );

			// Test it both ways
			aResult = aBox.GetUnion(bBox, NULL);
			bResult = bBox.GetUnion(aBox, NULL);			

			// In an ideal world - this will always be false, but then again - we wouldn't need this test harness either
			if( (aResult != bResult) || (aResult != data[idx].shouldPass) )
			{
				ATLTRACE("\nFound a problem with (%d,%d,%d,%d) (%d,%d,%d,%d) %d %d %d\n",
					aBox.m_left,aBox.m_top,aBox.m_right,aBox.m_bottom,
					bBox.m_left,bBox.m_top,bBox.m_right,bBox.m_bottom,
					aResult, bResult, data[idx].shouldPass );
				// Dump the rects to the output window so we can see what happened
				maxX = max(aBox.m_right, bBox.m_right);
				maxY = max(aBox.m_bottom, bBox.m_bottom);
				for( y = 0; y <= maxY; y++ )
				{					
					for( x = 0; x <= maxX; x++ )
					{
						character = space;

						if( (aBox.m_left  == x && aBox.m_top    == y) || (aBox.m_left  == x && aBox.m_bottom == y) ||
							(aBox.m_right == x && aBox.m_top    == y) || (aBox.m_right == x && aBox.m_bottom == y) )
						{
							character |= alpha;
						}
						
						if( (bBox.m_left  == x && bBox.m_top    == y) || (bBox.m_left  == x && bBox.m_bottom == y) ||
							(bBox.m_right == x && bBox.m_top    == y) || (bBox.m_right == x && bBox.m_bottom == y) )
						{
							character |= beta;
						}
						
						switch( character )
						{
						case space:   ATLTRACE("."); break;
						case alpha:   ATLTRACE("A"); break;
						case beta:    ATLTRACE("B"); break;
						case overlap: ATLTRACE("X"); break;
						default:      ATLTRACE("?");
						}
					}
					ATLTRACE("\n");
				}

				ATLASSERT(!"Not what we were expecting!");
				return false;
			}
		}

		return true;
	}
#endif //_DEBUG

	bool GetUnion(const TestRect & other, TestRect * result) const
	{
		TestRect calc;

		// Make sure we have area
		if( !HasArea() || !other.HasArea() )
			return false;

		// Check for identical
		if( IsEqual(other) )
		{
			if( result )
				result->SetRect(calc);
			return true;
		}

		// Disregard the obvious - boxes that are outside other boxes
		if( m_right < other.m_left )
			return false;
		if( m_bottom < other.m_top )
			return false;
		if( m_left > other.m_right )
			return false;
		if( m_top > other.m_bottom )
			return false;

		// Snap the edges to the minimum constraints of both boxes
		calc.m_left   = max(m_left,  other.m_left);
		calc.m_right  = min(m_right, other.m_right);
		calc.m_top    = max(m_top,   other.m_top);
		calc.m_bottom = min(m_bottom,other.m_bottom);

		// No area == no union
		if( !calc.HasArea() )
			return false;

		// Set the union result value?
		if( result )
			result->SetRect(calc);
	
		return true;
	}
};


#endif //__TEST_RECTANGLE_H__
