//--------------------------------------
//               PowerUI
//
//        For documentation or 
//    if you have any issues, visit
//        powerUI.kulestar.com
//
//    Copyright  2013 Kulestar Ltd
//          www.kulestar.com
//--------------------------------------

namespace Css{

	/// <summary>
	/// BoxRegion defines a 2D screen region. Mostly used for clipping.
	/// </summary>

	public class BoxRegion : ScreenRegion{
		
		/// <summary>An all zero box region.</summary>
		public static BoxRegion Empty{
			get{
				return new BoxRegion();
			}
		}
		
		/// <summary>The X coordinate of the box.</summary>
		public float X;
		/// <summary>The Y coordinate of the box.</summary>
		public float Y;
		/// <summary>The Maximum X value of the box. X+Width.</summary>
		public float MaxX;
		/// <summary>The Maximum Y value of the box. Y+Height.</summary>
		public float MaxY;
		/// <summary>The width of the box.</summary>
		public float Width;
		/// <summary>The height of the box.</summary>
		public float Height;
		
		
		public BoxRegion(){}
		
		/// <summary>Creates a new box.</summary>
		/// <param name="x">The X coordinate of the box.</param>
		/// <param name="y">The Y coordinate of the box.</param>
		/// <param name="width">The width of the box.</param>
		/// <param name="height">The height of the box.</param>
		public BoxRegion(float x,float y,float width,float height){
			Set(x,y,width,height);
		}
		
		public override float ScreenMinX{
			get{
				return X;
			}
		}
		
		public override float ScreenMinY{
			get{
				return Y;
			}
		}
		
		public override float ScreenMaxX{
			get{
				return MaxX;
			}
		}
		
		public override float ScreenMaxY{
			get{
				return MaxY;
			}
		}
		
		/// <summary>Checks if this box contains the given co-ordinates.</summary>
		/// <param name="x">The x coordinate to check.</param>
		/// <param name="y">The y coordinate to check.</param>
		/// <returns>True if the bounding box of this element contains the coordinate.</returns>
		public override bool Contains(float x,float y){
			return (x>=X && y>=Y && y<=MaxY && x<=MaxX);
		}
		
		/// <summary>Applies new values to an existing box.</summary>
		/// <param name="x">The X coordinate of the box.</param>
		/// <param name="y">The Y coordinate of the box.</param>
		/// <param name="width">The width of the box.</param>
		/// <param name="height">The height of the box.</param>
		public void Set(float x,float y,float width,float height){
			X=x;
			Y=y;
			Width=width;
			Height=height;
			MaxX=X+Width;
			MaxY=Y+Height;
		}
		
		public void SetPoints(float minX,float minY,float maxX,float maxY){
			X=minX;
			Y=minY;
			MaxX=maxX;
			MaxY=maxY;
			Width=MaxX-X;
			Height=MaxY-Y;
		}
		
		/// <summary>Sets this region to being the given point (with a w/h of 0).</summary>
		public void SetPoint(float x,float y){
			
			X=x;
			Y=y;
			MaxX=x;
			MaxY=y;
			Width=0;
			Height=0;
			
		}
		
		/// <summary>Adds the given point to the region.</summary>
		public void AddPoint(float x,float y){
			
			if(x<X){
				X=x;
			}
			
			if(y<Y){
				Y=y;
			}
			
			if(x>MaxX){
				MaxX=x;
			}
			
			if(y>MaxY){
				MaxY=y;
			}
			
			Width=MaxX-X;
			Height=MaxY-Y;
		}
		
		/// <summary>Adds the given region into this one.</summary>
		public void Combine(BoxRegion r){
			
			if(r.X<X){
				X=r.X;
			}
			
			if(r.Y<Y){
				Y=r.Y;
			}
			
			if(r.MaxX>MaxX){
				MaxX=r.MaxX;
			}
			
			if(r.MaxY>MaxY){
				MaxY=r.MaxY;
			}
			
			Width=MaxX-X;
			Height=MaxY-Y;
			
		}
		
		/// <summary>True if this region is non-zero.</summary>
		public bool IsNotEmpty{
			get{
				return (X!=0f || Y!=0f || Width!=0f || Height!=0f);
			}
		}
		
		/// <summary>True if this region is all zero.</summary>
		public bool IsEmpty{
			get{
				return (X==0f && Y==0f && Width==0f && Height==0f);
			}
		}
		
		/// <summary>Evaluates if one box overlaps another.</summary>
		/// <param name="box">The other box to check with.</param>
		/// <returns>True if the given box overlaps this one.</returns>
		public bool Overlaps(BoxRegion box){
			return (box.X<=MaxX && box.MaxX>=X && box.Y<=MaxY && box.MaxY>=Y);
		}
		
		/// <summary>Makes sure this box fits inside the given one by clipping it so it does.
		/// Any pixels sliced off this box are also sliced off affect.</summary>
		/// <param name="bound">The bounding box that this must be clipped to.</param>
		/// <param name="affect">A secondary box that will also be clipped with this one.
		/// May, for example, be a region of UV pixel coordinates.</param>
		public void ClipByAffecting(BoxRegion bound,BoxRegion affect){
			if(affect==null){
				// No second box - just clip normally.
				ClipBy(bound);
				return;
			}
			
			affect.RemoveFromLeft(ClipLeft(bound.X));
			affect.RemoveFromRight(ClipRight(bound.MaxX));
			// The bottom of the graphic is actually the TOP of our verts - it's inverted, so we simply do:
			affect.RemoveFromBottom(ClipTop(bound.Y));
			affect.RemoveFromTop(ClipBottom(bound.MaxY));
		}
		
		/// <summary>Makes sure this box fits inside the given one by clipping it so it does.</summary>
		/// <param name="bound">The bounding box that this must be clipped to.</param>
		public void ClipBy(BoxRegion box){
			ClipLeft(box.X);
			ClipRight(box.MaxX);
			ClipTop(box.Y);
			ClipBottom(box.MaxY);
		}
		
		/// <summary>Makes sure this box fits inside the given one by clipping it so it does.</summary>
		/// <param name="bound">The bounding box that this must be clipped to.</param>
		public bool ClipByChecked(BoxRegion box){
			bool clipped=(ClipLeft(box.X)!=0f);
			
			clipped|=(ClipRight(box.MaxX)!=0f);
			clipped|=(ClipTop(box.Y)!=0f);
			clipped|=(ClipBottom(box.MaxY)!=0f);
			
			return clipped;
		}
		
		/// <summary>Clips the left side of this box, making sure it does not extend beyond the given value.</summary>
		/// <param name="line">The x coordinate of a vertical line to clip with.
		/// It's exclusive; If it sits on the border, nothing happens.</param>
		/// <returns>The amount of pixels cut off.</returns>
		public float ClipLeft(float line){
			if(line>MaxX){
				// Clips the whole thing - the line is to the right of the box.
				float lastW=Width;
				X=MaxX;
				Width=0f;
				return lastW;
			}else if(line<=X){
				// No effect.
				return 0f;
			}
			float lastWidth=Width;
			X=line;
			Width=MaxX-X;
			return lastWidth-Width;
		}
		
		/// <summary>Clips the right side of this box, making sure it does not extend beyond the given value.</summary>
		/// <param name="line">The x coordinate of a vertical line to clip with.
		/// It's exclusive; If it sits on the border, nothing happens.</param>
		/// <returns>The amount of pixels cut off.</returns>
		public float ClipRight(float line){
			if(line>=MaxX){
				// No effect - the line is to the right of the box.
				return 0f;
			}else if(line<X){
				// Clips the whole thing - the line is to the left of the box.
				float lastW=Width;
				MaxX=X;
				Width=0f;
				return lastW;
			}
			float lastWidth=Width;
			MaxX=line;
			Width=MaxX-X;
			return lastWidth-Width;
		}
		
		/// <summary>Clips the top of this box, making sure it does not extend beyond the given value.</summary>
		/// <param name="line">The y coordinate of a horizontal line to clip with.
		/// It's exclusive; If it sits on the border, nothing happens.</param>
		/// <returns>The amount of pixels cut off.</returns>
		public float ClipTop(float line){
			if(line>MaxY){
				// Clips the whole thing - the line is below the box.
				// Warning! MaxY represents the bottom, not the top.
				float lastH=Height;
				Y=MaxY;
				Height=0f;
				return lastH;
			}else if(line<=Y){
				// No effect - the line is above the box.
				return 0f;
			}
			float lastHeight=Height;
			Y=line;
			Height=MaxY-Y;
			return lastHeight-Height;
		}
		
		/// <summary>Clips the bottom of this box, making sure it does not extend beyond the given value.</summary>
		/// <param name="line">The y coordinate of a horizontal line to clip with.
		/// It's exclusive; If it sits on the border, nothing happens.</param>
		/// <returns>The amount of pixels cut off.</returns>
		public float ClipBottom(float line){
			if(line>=MaxY){
				// No effect - the line is below the box.
				return 0f;
			}else if(line<Y){
				// Clips the whole thing - the line is above the box.
				// Warning! Y represents the top of the box, not the bottom.
				float lastH=Height;
				MaxY=Y;
				Height=0;
				return lastH;
			}
			float lastHeight=Height;
			MaxY=line;
			Height=MaxY-Y;
			return lastHeight-Height;
		}
		
		/// <summary>Removes up to the given amount of pixels from the left side of the box.
		/// It is bounded to ensure a negative width is not created.</summary>
		/// <param name="px">The amount of pixels to remove.</param>
		public void RemoveFromLeft(float px){
			if(px==0f){
				return;
			}
			if(px>Width){
				px=Width;
			}
			X+=px;
			Width-=px;
		}
		
		/// <summary>Removes up to the given amount of pixels from the right side of the box.
		/// It is bounded to ensure a negative width is not created.</summary>
		/// <param name="px">The amount of pixels to remove.</param>
		public void RemoveFromRight(float px){
			if(px==0f){
				return;
			}
			if(px>Width){
				px=Width;
			}
			MaxX-=px;
			Width-=px;
		}
		
		/// <summary>Removes up to the given amount of pixels from the top side of the box.
		/// It is bounded to ensure a negative height is not created.</summary>
		/// <param name="px">The amount of pixels to remove.</param>
		public void RemoveFromTop(float px){
			if(px==0f){
				return;
			}
			if(px>Height){
				px=Height;
			}
			Y+=px;
			Height-=px;
		}
		
		/// <summary>Removes up to the given amount of pixels from the bottom side of the box.
		/// It is bounded to ensure a negative height is not created.</summary>
		/// <param name="px">The amount of pixels to remove.</param>
		public void RemoveFromBottom(float px){
			if(px==0f){
				return;
			}
			if(px>Height){
				px=Height;
			}
			MaxY-=px;
			Height-=px;
		}
		
		/// <summary>Produces an easy to read summary of this boxes shape.</summary>
		public override string ToString(){
			return "x: "+X+",y: "+Y+",width: "+Width+", height: "+Height;
		}
		
	}
	
}