package org.cocos2d.cocoa;

import org.cocos2d.types.CGAffineTransform;

/**
 * @addtogroup data_structures
 * @{
 */

public class CCGeometry {

	public static class CCPoint {

		public static CCPoint Make(float x, float y) {
			return new CCPoint(x, y);
		}

		public static final CCPoint Zero = CCPoint.Make(0.0f, 0.0f);

		public float x;
		public float y;

		public CCPoint() {
			this.setPoint(0.0f, 0.0f);
		}

		public CCPoint(float x, float y) {
			this.setPoint(x, y);
		}

		public CCPoint(final CCPoint point) {
			this.setPoint(point);
		}

		public void setPoint(float x, float y) {
			this.x = x;
			this.y = y;
		}

		public void setPoint(final CCPoint point) {
			this.setPoint(point.x, point.y);
		}

		public boolean equals(float x, float y) {
			return ((this.x == x) && (this.y == y));
		}

		public boolean equals(final CCPoint point) {
			return this.equals(point.x, point.y);
		}

		// TODO legacy -->
		public static CCPoint applyAffineTransform(CCPoint aPoint, CGAffineTransform aTransform) {
			return aTransform.applyTransform(aPoint);
		}
		// TODO legacy <--
	}

	public static class CCSize {

		public static CCSize Make(float width, float height) {
			return new CCSize(width, height);
		}

		public static final CCSize Zero = CCSize.Make(0.0f, 0.0f);

		public float width;
		public float height;

		public CCSize() {
			this.setSize(0.0f, 0.0f);
		}

		public CCSize(float width, float height) {
			this.setSize(width, height);
		}

		public CCSize(final CCSize size) {
			this.setSize(size);
		}

		public void setSize(float width, float height) {
			this.width = width;
			this.height = height;
		}

		public void setSize(final CCSize size) {
			this.setSize(size.width, size.height);
		}

		public boolean equals(float width, float height) {
			return ((this.width == width) && (this.height == height));
		}

		public boolean equals(final CCSize size) {
			return this.equals(size.width, size.height);
		}
	}

	public static class CCRect {

		public static CCRect Make(float x, float y, float width, float height) {
			return new CCRect(x, y, width, height);
		}

		public static final CCRect Zero = CCRect.Make(0.0f, 0.0f, 0.0f, 0.0f);

		public CCPoint origin = new CCPoint();
		public CCSize size = new CCSize();

		public CCRect() {
			this.setRect(0.0f, 0.0f, 0.0f, 0.0f);
		}

		public CCRect(float x, float y, float width, float height) {
			this.setRect(x, y, width, height);
		}

		public CCRect(CCPoint origin, CCSize size) {
			this.setRect(origin, size);
		}

		public CCRect(final CCRect rect) {
			this.setRect(rect);
		}

		public void setRect(float x, float y, float width, float height) {
			this.origin.setPoint(x, y);
			this.size.setSize(width, height);
		}

		public void setRect(CCPoint origin, CCSize size) {
			this.setRect(origin.x, origin.y, size.width, size.height);
		}

		public void setRect(final CCRect rect) {
			this.setRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
		}

		/// return the leftmost x-value of current rect
		public float getMinX() {
			return (this.origin.x);
		}

		/// return the midpoint x-value of current rect
		public float getMidX() {
			return (this.origin.x + (this.size.width / 2.0f));
		}

		/// return the rightmost x-value of current rect
		public float getMaxX() {
			return (this.origin.x + this.size.width);
		}

		/// return the bottommost y-value of current rect
		public float getMinY() {
			return (this.origin.y);
		}

		/// return the midpoint y-value of current rect
		public float getMidY() {
			return (this.origin.y + (this.size.height / 2.0f));
		}

		/// return the topmost y-value of current rect
		public float getMaxY() {
			return (this.origin.y + this.size.height);
		}

		public boolean equals(float x, float y, float width, float height) {
			return (this.origin.equals(x, y) && this.size.equals(width, height));
		}

		public boolean equals(final CCRect rect) {
			return this.equals(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
		}

		public boolean containsPoint(float x, float y) {
			return (	(this.getMinX() <= x) && (x <= this.getMaxX())
					&&	(this.getMinY() <= y) && (y <= this.getMaxY())	);
		}

		public boolean containsPoint(final CCPoint point) {
			return this.containsPoint(point.x, point.y);
		}

		public boolean intersectsRect(final CCRect rect) {
			return !(	this.getMaxX() < rect.getMinX() ||
						rect.getMaxX() < this.getMinX() ||
						this.getMaxY() < rect.getMinY() ||
						rect.getMaxY() < this.getMinY()	);
		}

		// TODO legacy -->
		public static CCRect applyAffineTransform(CCRect aRect, CGAffineTransform matrix) {
			CCRect r = new CCRect();
			CCPoint [] p = new CCPoint[4];

			for (int i = 0; i < 4; i++) {
				p[i].setPoint(aRect.origin);
			}

			p[1].x += aRect.size.width;
			p[2].y += aRect.size.height;
			p[3].x += aRect.size.width;
			p[3].y += aRect.size.height;

			for (int i = 0; i < 4; i++) {
				p[i] = CCPoint.applyAffineTransform(p[i], matrix);
			}

			CCPoint min = new CCPoint(p[0].x, p[0].y),
					max = new CCPoint(p[0].x, p[0].y);
			for (int i = 1; i < 4; i++) {
				min.x = Math.min(min.x, p[i].x);
				min.y = Math.min(min.y, p[i].y);
				max.x = Math.max(max.x, p[i].x);
				max.y = Math.max(max.y, p[i].y);
			}

			r.origin.x = min.x;
			r.origin.y = min.y;
			r.size.width = max.x - min.x;
			r.size.height = max.y - min.y;

			return r;
		}
		// TODO legacy <--

	}
}

// end of data_structure group
/// @}
