package {
	import flash.geom.Point;
	import charts.Elements.Point;
	
	public class ScreenCoords extends ScreenCoordsBase
	{

		
		
		private var x_range:Range;
		private var y_range:Range;
		private var y_right_range:Range;
		
		// position of the zero line
		//public var zero:Number=0;
		//public var steps:Number=0;
		
		// tick_offset is set by 3D axis
		public var tick_offset:Number;
		private var x_offset:Boolean;
		private var y_offset:Object;
		private var bar_groups:Number;
	
		
		public function ScreenCoords( top:Number, left:Number, right:Number, bottom:Number,
							y_axis_range:Range,
							y_axis_right_range:Range,
							x_axis_range:Range,
							x_left_label_width:Number, x_right_label_width:Number,
							three_d:Boolean,
							x_offset:Boolean, y_offset:Object )
		{
			super( top, left, right, bottom );
			
			var tmp_left:Number = left;
			
			this.x_range = x_axis_range;
			this.y_range = y_axis_range;
			this.y_right_range = y_axis_right_range;
			
			// tr.ace( '-----' );
			// tr.ace( this.x_range.count() );
			// tr.ace( this.y_range.count() );
			
			
			if( x_range ) {
				right = this.jiggle( left, right, x_right_label_width, x_axis_range.count() );
				tmp_left = this.shrink_left( left, right, x_left_label_width, x_axis_range.count() );
			}
			
			this.top = top;
			this.left = Math.max(left,tmp_left);
			
			// round this down to the nearest int:
			this.right = Math.floor( right );
			this.bottom = bottom;
			this.width = this.right-this.left;
			this.height = bottom-top;
			
			if( three_d )
			{
				// tell the box object that the
				// X axis labels need to be offset
				this.tick_offset = 12;
			}
			else
				this.tick_offset = 0;
			
			//
			//  x_offset:
			//
			//   False            True
			//
			//  |               |
			//  |               |
			//  |               |
			//  +--+--+--+      |-+--+--+--+-+
			//  0  1  2  3        0  1  2  3
			//
			this.x_offset = x_offset;
			this.y_offset = y_offset;
				
			this.bar_groups = 1;
		}
		
		//
		// if the last X label is wider than the chart area, the last few letters will
		// be outside the drawing area. So we make the chart width smaller so the label
		// will fit into the screen.
		//
		public function jiggle( left:Number, right:Number, x_label_width:Number, count:Number ): Number {
			var r:Number = 0;

			if( x_label_width != 0 )
			{
				var item_width:Number = (right-left) / count;
				r = right - (item_width / 2);
				var new_right:Number = right;
				
				// while the right most X label is off the edge of the
				// Stage, move the box.right - 1
				while( r+(x_label_width/2) > right )
				{
					new_right -= 1;
					// changing the right also changes the item_width:
					item_width = (new_right-left) / count;
					r = new_right-(item_width/2);
				}
				right = new_right;
			}
			return right;
		}
		
		//
		// if the left label is truncated, shrink the box until
		// it fits onto the screen
		//
		public function shrink_left( left:Number, right:Number, x_label_width:Number, count:Number ): Number {
			var pos:Number = 0;

			if( x_label_width != 0 )
			{
				var item_width:Number = (right-left) / count;
				pos = left+(item_width/2);
				var new_left:Number = left;
				
				// while the left most label is hanging off the Stage
				// move the box.left in one pixel:
				while( pos-(x_label_width/2) < 0 )
				{
					new_left += 1;
					// changing the left also changes the item_width:
					item_width = (right-new_left) / count;
					pos = new_left+(item_width/2);
				}
				left = new_left;
			}
			
			return left;
			
		}
		
		//
		// the bottom point of a bar:
		//   min=-100 and max=100, use b.zero
		//   min = 10 and max = 20, use b.bottom
		//
		public override function get_y_bottom( right_axis:Boolean = false ):Number
		{
			//
			// may have min=10, max=20, or
			// min = 20, max = -20 (upside down chart)
			//
			var r:Range = right_axis ? this.y_right_range : this.y_range;
			
			var min:Number = r.min;
			var max:Number = r.max;
			min = Math.min( min, max );
			
			return this.get_y_from_val( Math.max(0,min), right_axis );
		}
		
		// takes a value and returns the screen Y location
		public function getY_old( i:Number, right_axis:Boolean ):Number
		{
			var r:Range = right_axis ? this.y_right_range : this.y_range;
			
			var steps:Number = this.height / (r.count());// ( right_axis ));
			
			// find Y pos for value=zero
			var y:Number = this.bottom-(steps*(r.min*-1));
			
			// move up (-Y) to our point (don't forget that y_min will shift it down)
			y -= i*steps;
			return y;
		}
		
		//
		// takes a value and returns the screen Y location
		// what is the Y range?
		//
		// Horizontal bar charts are offset. Note:
		//   step = 1
		//   and step/2 is offset at the bottom and top
		// so we add 1*step so we can calculate:
		//
		//   offset = true 
		//
		//     |
		//  X -|==========
		//     |
		//  Y -|===
		//     |
		//  Z -|========
		//     +--+--+--+--+--+--
		//
		// offset = false
		//
		//  2 -|
		//     |
		//  1 -|  0--0--0--0--0
		//     |
		//  0 -+--+--+--+--+--+--
		//
		public override function get_y_from_val( i:Number, right_axis:Boolean = false ):Number {
			
			var r:Range = right_axis ? this.y_right_range : this.y_range;
			
			var count:Number = r.count() + ( this.y_offset.offset ? 1 : 0 );
			var steps:Number = this.height / count;
			
			// tr.ace( 'off' );
			// tr.ace( this.y_offset.offset );
			// tr.ace( count );
			
			var tmp:Number = 0;
			if( this.y_offset.offset )
				tmp = (steps / 2);
				
			// move up (-Y) to our point (don't forget that y_min will shift it down)
			return this.bottom-tmp-(r.min-i)*steps*-1;
		}
		
		public override function get_get_x_from_pos_and_y_from_val( index:Number, y:Number, right_axis:Boolean = false ):flash.geom.Point {
			
			return new flash.geom.Point(
				this.get_x_from_pos( index ),
				this.get_y_from_val( y, right_axis ) );
		}
		
		public function width_():Number
		{
			return this.right-this.left_();
		}
		
		private function left_():Number
		{
			var padding_left:Number = this.tick_offset;
			return this.left+padding_left;
		}
		
		//
		// Scatter and Horizontal Bar charts use this:
		//
		//   get the x position by value
		//  (e.g. what is the x position for -5 ?)
		//
		public override function get_x_from_val( i:Number ):Number {

			var item_width:Number = this.width_() / this.x_range.count();
			
			var pos:Number = i-this.x_range.min;
			
			var tmp:Number = 0;
			if( this.x_offset )
				tmp = (item_width/2);
				
			return this.left_()+tmp+(pos*item_width);
		}
		
		//
		// get the x location of the n'th item
		//
		public override function get_x_from_pos( i:Number ):Number {
			
			var item_width:Number = this.width_() / this.x_range.count();
			
			var tmp:Number = 0;
			if( this.x_offset )
				tmp = (item_width/2);

			return this.left_()+tmp+(i*item_width);
		}
		
		//
		// get the position of the n'th X axis tick
		//
		public function get_x_tick_pos( i:Number ):Number
		{
			return this.get_x_from_pos(i) - this.tick_offset;
		}
	
		
		//
		// make a point object, using the absolute values (e.g. -5,-5 )
		//
		public function make_point_2( x:Number, y:Number, right_axis:Boolean ):charts.Elements.Point
		{
			return new charts.Elements.Point(
				this.get_x_from_val( x ),
				this.get_y_from_val( y, right_axis )
				
				// whats this for?
				//,y
				);
		}
		
		public function set_bar_groups( n:Number ): void {
			this.bar_groups = n;
		}
		
		//
		// index: the n'th bar from the left
		//
		public function get_bar_coords( index:Number, group:Number ):Object {
			var item_width:Number = this.width_() / this.x_range.count();
			
			// the bar(s) have gaps between them:
			var bar_set_width:Number = item_width*0.8;
			
			// get the margin between sets of bars:
			var tmp:Number = 0;
			if( this.x_offset )
				tmp = item_width;
			
			// 1 bar == 100% wide, 2 bars = 50% wide each
			var bar_width:Number = bar_set_width / this.bar_groups;
			//bar_width -= 0.001;		// <-- hack so bars don't quite touch
			
			var bar_left:Number = this.left_()+((tmp-bar_set_width)/2);
			var left:Number = bar_left+(index*item_width);
			left += bar_width * group;
			
			return { x:left, width:bar_width };
		}
		
		public function get_horiz_bar_coords( index:Number, group:Number ):Object {
			
			// split the height into equal heights for each bar
			var bar_width:Number = this.height / (this.y_range.count()+1);
			
			// the bar(s) have gaps between them:
			var bar_set_width:Number = bar_width*0.8;
			
			// 1 bar == 100% wide, 2 bars = 50% wide each
			var group_width:Number = bar_set_width / this.bar_groups;
			
			var bar_top:Number = this.top+((bar_width-bar_set_width)/2);
			var top:Number = bar_top+(index*bar_width);
			top += group_width * group;
			
			return { y:top, width:group_width };
		}
		
		
		public function makePointHLC( x:Number, high:Number, close:Number, low:Number, right_axis:Boolean, group:Number, group_count:Number )
		:PointHLC {
	
			var item_width:Number = this.width_() / this.x_range.count();
			// the bar(s) have gaps between them:
			var bar_set_width:Number = item_width*1;

			// get the margin between sets of bars:
			var bar_left:Number = this.left_()+((item_width-bar_set_width)/2);
			// 1 bar == 100% wide, 2 bars = 50% wide each
			var bar_width:Number = bar_set_width/group_count;

			var left:Number = bar_left+(x*item_width);
			left += bar_width*group;

			return new PointHLC(
				left,
				this.get_y_from_val( high, right_axis ),
				this.get_y_from_val( close, right_axis ),
				this.get_y_from_val( low, right_axis ),
				high,
				bar_width
//				,close
				);
	
		}
	}
}