public class

IntQuadTree

extends QuadTree<B extends IBounds, T extends ISpatialItem<B>>
implements IIntBounds
package org.andengine.util.adt.spatial.quadtree;

import java.util.ArrayList;
import java.util.List;

import org.andengine.util.IMatcher;
import org.andengine.util.adt.bounds.BoundsSplit;
import org.andengine.util.adt.bounds.IIntBounds;
import org.andengine.util.adt.bounds.IntBounds;
import org.andengine.util.adt.bounds.BoundsSplit.BoundsSplitException;
import org.andengine.util.adt.spatial.ISpatialItem;
import org.andengine.util.adt.spatial.bounds.util.IntBoundsUtils;


/**
 * (c) Zynga 2011
 *
 * @author Nicolas Gramlich <ngramlich@zynga.com>
 * @since 20:22:18 - 10.10.2011
 */
public class IntQuadTree<T extends ISpatialItem<IIntBounds>> extends QuadTree<IIntBounds, T> implements IIntBounds {
	// ===========================================================
	// Constants
	// ===========================================================

	// ===========================================================
	// Fields
	// ===========================================================

	private final IntBounds mQueryIntBounds = new IntBounds(0, 0, 0, 0);

	// ===========================================================
	// Constructors
	// ===========================================================

	public IntQuadTree(final IIntBounds pIntBounds) {
		super(pIntBounds);
	}

	public IntQuadTree(final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
		super(new IntBounds(pXMin, pYMin, pXMax, pYMax));
	}

	public IntQuadTree(final IIntBounds pIntBounds, final int pMaxLevel) {
		super(pIntBounds, pMaxLevel);
	}

	public IntQuadTree(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final int pMaxLevel) {
		super(new IntBounds(pXMin, pYMin, pXMax, pYMax), pMaxLevel);
	}

	@Override
	protected IntQuadTreeNode initRoot(final IIntBounds pIntBounds) {
		return new IntQuadTreeNode(QuadTree.LEVEL_ROOT, pIntBounds);
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	@Override
	public int getXMin() {
		return this.getRoot().getXMin();
	}

	@Override
	public int getYMin() {
		return this.getRoot().getYMin();
	}

	@Override
	public int getXMax() {
		return this.getRoot().getXMax();
	}

	@Override
	public int getYMax() {
		return this.getRoot().getYMax();
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@SuppressWarnings("unchecked")
	@Override
	protected IntQuadTreeNode getRoot() {
		return (IntQuadTreeNode) this.mRoot;
	}

	// ===========================================================
	// Methods
	// ===========================================================

	public synchronized ArrayList<T> query(final int pX, final int pY) {
		this.mQueryIntBounds.set(pX, pY);
		return this.query(this.mQueryIntBounds);
	}

	public synchronized <L extends List<T>> L query(final int pX, final int pY, final L pResult) {
		this.mQueryIntBounds.set(pX, pY);
		return this.query(this.mQueryIntBounds, pResult);
	}

	public synchronized ArrayList<T> query(final int pX, final int pY, final IMatcher<T> pMatcher) {
		this.mQueryIntBounds.set(pX, pY);
		return this.query(this.mQueryIntBounds, pMatcher);
	}

	public synchronized <L extends List<T>> L query(final int pX, final int pY, final IMatcher<T> pMatcher, final L pResult) {
		this.mQueryIntBounds.set(pX, pY);
		return this.query(this.mQueryIntBounds, pMatcher, pResult);
	}

	public synchronized ArrayList<T> query(final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.query(this.mQueryIntBounds);
	}

	public synchronized <L extends List<T>> L query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final L pResult) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.query(this.mQueryIntBounds, pResult);
	}

	public synchronized ArrayList<T> query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.query(this.mQueryIntBounds, pMatcher);
	}

	public synchronized <L extends List<T>> L query(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher, final L pResult) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.query(this.mQueryIntBounds, pMatcher, pResult);
	}

	public synchronized <L extends List<S>, S extends T> L queryForSubclass(final int pX, final int pY, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException {
		this.mQueryIntBounds.set(pX, pY);
		return this.queryForSubclass(this.mQueryIntBounds, pMatcher, pResult);
	}

	public synchronized <L extends List<S>, S extends T> L queryForSubclass(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher, final L pResult) throws ClassCastException {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.queryForSubclass(this.mQueryIntBounds, pMatcher, pResult);
	}

	public synchronized boolean containsAny(final int pX, final int pY) {
		this.mQueryIntBounds.set(pX, pY);
		return this.containsAny(this.mQueryIntBounds);
	}

	public synchronized boolean containsAny(final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.containsAny(this.mQueryIntBounds);
	}

	public synchronized boolean containsAny(final int pX, final int pY, final IMatcher<T> pMatcher) {
		this.mQueryIntBounds.set(pX, pY);
		return this.containsAny(this.mQueryIntBounds, pMatcher);
	}

	public synchronized boolean containsAny(final int pXMin, final int pYMin, final int pXMax, final int pYMax, final IMatcher<T> pMatcher) {
		this.mQueryIntBounds.set(pXMin, pYMin, pXMax, pYMax);
		return this.containsAny(this.mQueryIntBounds, pMatcher);
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	public class IntQuadTreeNode extends QuadTreeNode implements IIntBounds {
		// ===========================================================
		// Constants
		// ===========================================================

		// ===========================================================
		// Fields
		// ===========================================================

		private final int mXMin;
		private final int mYMin;
		private final int mXMax;
		private final int mYMax;

		// ===========================================================
		// Constructors
		// ===========================================================

		public IntQuadTreeNode(final int pLevel, final IIntBounds pIntBounds) {
			this(pLevel, pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax());
		}

		public IntQuadTreeNode(final int pLevel, final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
			super(pLevel);

			this.mXMin = pXMin;
			this.mYMin = pYMin;
			this.mXMax = pXMax;
			this.mYMax = pYMax;

			if(pXMin > pXMax) {
				throw new IllegalArgumentException("pXMin must be smaller or equal to pXMax.");
			}
			if(pYMin > pYMax) {
				throw new IllegalArgumentException("pYMin must be smaller or equal to pYMax.");
			}
		}

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		@Override
		public int getXMin() {
			return this.mXMin;
		}
		
		@Override
		public int getYMin() {
			return this.mYMin;
		}

		@Override
		public int getXMax() {
			return this.mXMax;
		}

		@Override
		public int getYMax() {
			return this.mYMax;
		}

		public int getWidth() {
			return this.mXMax - this.mXMin + 1;
		}

		public int getHeight() {
			return this.mYMax - this.mYMin + 1;
		}

		// ===========================================================
		// Methods for/from SuperClass/Interfaces
		// ===========================================================

		@Override
		protected IntQuadTreeNode split(final BoundsSplit pBoundsSplit) {
			final int width = this.getWidth();
			final int height = this.getHeight();

			if(width <= 2 && height <= 2) {
				throw new BoundsSplitException();
			}

			final int xMin = this.getXMin(pBoundsSplit);
			final int yMin = this.getYMin(pBoundsSplit);
			final int xMax = this.getXMax(pBoundsSplit);
			final int yMax = this.getYMax(pBoundsSplit);

			return new IntQuadTreeNode(this.mLevel + 1, xMin, yMin, xMax, yMax);
		}

		@Override
		protected boolean contains(final IIntBounds pIntBounds) {
			return this.contains(pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax());
		}

		@Override
		protected boolean contains(final BoundsSplit pBoundsSplit, final IIntBounds pIntBounds) {
			return IntBoundsUtils.contains(this.getXMin(pBoundsSplit), this.getYMin(pBoundsSplit), this.getXMax(pBoundsSplit), this.getYMax(pBoundsSplit), pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax());
		}

		@Override
		protected boolean intersects(final IIntBounds pIntBounds) {
			return IntBoundsUtils.intersects(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pIntBounds.getXMin(), pIntBounds.getYMin(), pIntBounds.getXMax(), pIntBounds.getYMax());
		}

		@Override
		protected boolean intersects(final IIntBounds pIntBoundsA, final IIntBounds pIntBoundsB) {
			return IntBoundsUtils.intersects(pIntBoundsA, pIntBoundsB);
		}

		@Override
		protected boolean containedBy(final IIntBounds pBounds) {
			return IntBoundsUtils.contains(pBounds.getXMin(), pBounds.getYMin(), pBounds.getXMax(), pBounds.getYMax(), this.mXMin, this.mYMin, this.mXMax, this.mYMax);
		}

		@Override
		protected void appendBoundsToString(final StringBuilder pStringBuilder) {
			pStringBuilder
				.append("[XMin: ")
				.append(this.mXMin)
				.append(", YMin: ")
				.append(this.mYMin)
				.append(", XMax: ")
				.append(this.mXMax)
				.append(", YMax: ")
				.append(this.mYMax)
				.append("]");
		}

		// ===========================================================
		// Methods
		// ===========================================================

		private int getXMin(final BoundsSplit pBoundsSplit) {
			final int width = this.getWidth();
			final int halfWidth = width / 2;

			if(width <= 2) {
				switch(pBoundsSplit) {
					case TOP_LEFT:
					case BOTTOM_LEFT:
						return this.mXMin;
					case BOTTOM_RIGHT:
					case TOP_RIGHT:
						throw new BoundsSplitException();
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			} else {
				switch(pBoundsSplit) {
					case TOP_LEFT:
						return this.mXMin;
					case TOP_RIGHT:
						return this.mXMin + halfWidth;
					case BOTTOM_LEFT:
						return this.mXMin;
					case BOTTOM_RIGHT:
						return this.mXMin + halfWidth;
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			}
		}
		
		private int getYMin(final BoundsSplit pBoundsSplit) {
			final int height = this.getHeight();
			final int halfHeight = height / 2;
			
			if(height <= 2) {
				switch(pBoundsSplit) {
					case TOP_LEFT:
					case TOP_RIGHT:
						return  this.mYMin;
					case BOTTOM_LEFT:
					case BOTTOM_RIGHT:
						throw new BoundsSplitException();
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			} else {
				switch(pBoundsSplit) {
					case TOP_LEFT:
						return  this.mYMin;
					case TOP_RIGHT:
						return this.mYMin;
					case BOTTOM_LEFT:
						return this.mYMin + halfHeight;
					case BOTTOM_RIGHT:
						return this.mYMin + halfHeight;
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			}
		}

		private int getXMax(final BoundsSplit pBoundsSplit) {
			final int width = this.getWidth();
			final int halfWidth = width / 2;

			if(width <= 2) {
				switch(pBoundsSplit) {
					case TOP_LEFT:
					case BOTTOM_LEFT:
						return this.mXMax;
					case BOTTOM_RIGHT:
					case TOP_RIGHT:
						throw new BoundsSplitException();
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			} else {
				switch(pBoundsSplit) {
					case TOP_LEFT:
						return this.mXMin + halfWidth;
					case TOP_RIGHT:
						return this.mXMax;
					case BOTTOM_LEFT:
						return this.mXMin + halfWidth;
					case BOTTOM_RIGHT:
						return this.mXMax;
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			}
		}

		private int getYMax(final BoundsSplit pBoundsSplit) {
			final int height = this.getHeight();
			final int halfHeight = height / 2;

			if(height <= 2) {
				switch(pBoundsSplit) {
					case TOP_LEFT:
					case TOP_RIGHT:
						return this.mYMax;
					case BOTTOM_LEFT:
					case BOTTOM_RIGHT:
						throw new BoundsSplitException();
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			} else {
				switch(pBoundsSplit) {
					case TOP_LEFT:
						return this.mYMin + halfHeight;
					case TOP_RIGHT:
						return this.mYMin + halfHeight;
					case BOTTOM_LEFT:
						return this.mYMax;
					case BOTTOM_RIGHT:
						return this.mYMax;
					default:
						throw new IllegalArgumentException("Unexpected " + BoundsSplit.class.getSimpleName() + ": '" + pBoundsSplit + "'.");
				}
			}
		}

		public boolean intersects(final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
			return IntBoundsUtils.intersects(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pXMin, pYMin, pXMax, pYMax);
		}

		public boolean contains(final int pXMin, final int pYMin, final int pXMax, final int pYMax) {
			return IntBoundsUtils.contains(this.mXMin, this.mYMin, this.mXMax, this.mYMax, pXMin, pYMin, pXMax, pYMax);
		}

		// ===========================================================
		// Inner and Anonymous Classes
		// ===========================================================
	}
}