package charts {

	import flash.events.Event;
	import flash.events.MouseEvent;
	import charts.series.Element;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	
	import charts.series.dots.DefaultDotProperties;
	import charts.series.dots.dot_factory;
	
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import charts.series.dots.PointDotBase;
	
	public class Line extends Base
	{
		// JSON style:
		protected var props:Properties;
		private var dot_style:Properties;
		private var on_show:Properties;
		private var line_style:LineStyle;
	
		private var on_show_timer:Timer;
		private var on_show_start:Boolean;
		
		public function Line( json:Object ) {
		
			var root:Properties = new Properties({
				values: 		[],
				width:			2,
				colour: 		'#3030d0',
				text: 			'',		// <-- default not display a key
				'font-size': 	12,
				tip:			'#val#',
				loop:			false,
				axis:			'left'
			});
			this.props = new Properties(json, root);
			
			this.line_style = new LineStyle(json['line-style']);
			this.dot_style = new DefaultDotProperties(json['dot-style'], this.props.get('colour'), this.props.get('axis'));
			
			//
			// see scatter base
			//
			var on_show_root:Properties = new Properties( {
				type:		"none",		// "pop-up",
				cascade:	0.5,
				delay:		0
				});
			this.on_show = new Properties(json['on-show'], on_show_root);
			this.on_show_start = true;// this.on_show.get('type');
			//
			//
			
			this.key		= this.props.get('text');
			this.font_size	= this.props.get('font-size');
			
			this.values = this.props.get('values');
			this.add_values();

			//
			// this allows the dots to erase part of the line
			//
			this.blendMode = BlendMode.LAYER;
		}
		
		//
		// called from the Base object
		//
		protected override function get_element( index:Number, value:Object ): Element {

			if ( value is Number )
				value = { value:value };
			
			var tmp:Properties = new Properties(value, this.dot_style);
			
			// Minor hack, replace all #key# with this key text,
			// we do this *after* the merge.
			tmp.set( 'tip', tmp.get('tip').replace('#key#', this.key) );
			
			// attach the animation bits:
			tmp.set('on-show', this.on_show);
				
			return dot_factory.make( index, tmp );
		}
		
		
		// Draw lines...
		public override function resize( sc:ScreenCoordsBase ): void {
			this.x = this.y = 0;

			this.move_dots(sc);
			
			if ( this.on_show_start )
				this.start_on_show_timer();
			else
				this.draw();
			
		}
	
		//
		// this is a bit dirty, as the dots animate we draw the line 60 times a second
		//
		private function start_on_show_timer(): void {
			this.on_show_start = false;
			this.on_show_timer = new Timer(1000 / 60);	// <-- 60 frames a second = 1000ms / 60
			this.on_show_timer.addEventListener("timer", animationManager);
			// Start the timer
			this.on_show_timer.start();
		}
		
		protected function animationManager(eventArgs:TimerEvent): void {
			
			this.draw();
			
			if( !this.still_animating() ) {
				tr.ace( 'Line.as : on show animation stop' );
				this.on_show_timer.stop();
			}
		}
		
		private function still_animating(): Boolean {
			var i:Number;
			var tmp:Sprite;
		
			for ( i=0; i < this.numChildren; i++ ) {

				tmp = this.getChildAt(i) as Sprite;
				
				// filter out the line masks
				if( tmp is PointDotBase )
				{
					var e:PointDotBase = tmp as PointDotBase;
					if ( e.is_tweening() )
						return true;
				}
			}
			return false;
		}
		
		//
		// this is called from both resize and the animation manager
		//
		protected function draw(): void {
			this.graphics.clear();
			this.draw_line();
		}
		
		// this is also called from area
		protected function draw_line(): void {
			
			
			this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('colour') );
			
			if( this.line_style.style != 'solid' )
				this.dash_line();
			else
				this.solid_line();
		
		}
		
		public function move_dots( sc:ScreenCoordsBase ): void {
			
			var i:Number;
			var tmp:Sprite;
		
			for ( i=0; i < this.numChildren; i++ ) {

				tmp = this.getChildAt(i) as Sprite;
				
				// filter out the line masks
				if( tmp is Element )
				{
					var e:Element = tmp as Element;
					
					// tell the point where it is on the screen
					// we will use this info to place the tooltip
					e.resize( sc );
				}
			}
		}
		
		public function solid_line(): void {
			
			var first:Boolean = true;
			var i:Number;
			var tmp:Sprite;
			var x:Number;
			var y:Number;
			
			for ( i=0; i < this.numChildren; i++ ) {

				tmp = this.getChildAt(i) as Sprite;
				
				// filter out the line masks
				if( tmp is Element )
				{
					var e:Element = tmp as Element;
					
					if( first )
					{
						this.graphics.moveTo(e.x, e.y);
						x = e.x;
						y = e.y;
						first = false;
					}
					else
						this.graphics.lineTo(e.x, e.y);
				}
			}
			
			if ( this.props.get('loop') ) {
				// close the line loop (radar charts)
				this.graphics.lineTo(x, y);
			}
		}
		
		// Dashed lines by Arseni
		public function dash_line(): void {
			
			var first:Boolean = true;
			
			var prev_x:int = 0;
			var prev_y:int = 0;
			var on_len_left:Number = 0;
			var off_len_left:Number = 0;
			var on_len:Number = this.line_style.on; //Stroke Length
			var off_len:Number = this.line_style.off; //Space Length
			var now_on:Boolean = true;
			
			for ( var i:Number = 0; i < this.numChildren; i++ ) {				
				var tmp:Sprite = this.getChildAt(i) as Sprite;				
				//
				// filter out the line masks
				//
				if( tmp is Element )
				{
					var e:Element = tmp as Element;
					
					if( first )
					{
						this.graphics.moveTo(e.x, e.y);
						on_len_left = on_len;
						off_len_left = off_len;
						now_on = true;
						first = false;
						prev_x = e.x;
						prev_y = e.y;
						var x_tmp_1:Number = prev_x;
						var x_tmp_2:Number;
						var y_tmp_1:Number = prev_y;
						var y_tmp_2:Number;						
					}
					else {
						var part_len:Number = Math.sqrt((e.x - prev_x) * (e.x - prev_x) + (e.y - prev_y) * (e.y - prev_y) );
						var sinus:Number = ((e.y - prev_y) / part_len); 
						var cosinus:Number = ((e.x - prev_x) / part_len); 
						var part_len_left:Number = part_len;
						var inside_part:Boolean = true;
							
						while (inside_part) {
							//Draw Lines And spaces one by one in loop
							if ( now_on ) {
								//Draw line
								//If whole stroke fits
								if (  on_len_left < part_len_left ) {
									//Fits - draw whole stroke
									x_tmp_2 = x_tmp_1 + on_len_left * cosinus;
									y_tmp_2 = y_tmp_1 + on_len_left * sinus;
									x_tmp_1 = x_tmp_2;
									y_tmp_1 = y_tmp_2;
									part_len_left = part_len_left - on_len_left;
									now_on = false;
									off_len_left = off_len;															
								} else {
									//Does not fit - draw part of the stroke
									x_tmp_2 = e.x;
									y_tmp_2 = e.y;
									x_tmp_1 = x_tmp_2;
									y_tmp_1 = y_tmp_2;
									on_len_left = on_len_left - part_len_left;
									inside_part = false;									
								}
								this.graphics.lineTo(x_tmp_2, y_tmp_2);								
							} else {
								//Draw space
								//If whole space fits
								if (  off_len_left < part_len_left ) {
									//Fits - draw whole space
									x_tmp_2 = x_tmp_1 + off_len_left * cosinus;
									y_tmp_2 = y_tmp_1 + off_len_left * sinus;
									x_tmp_1 = x_tmp_2;
									y_tmp_1 = y_tmp_2;
									part_len_left = part_len_left - off_len_left;								
									now_on = true;
									on_len_left = on_len;
								} else {
									//Does not fit - draw part of the space
									x_tmp_2 = e.x;									
									y_tmp_2 = e.y;									
									x_tmp_1 = x_tmp_2;
									y_tmp_1 = y_tmp_2;
									off_len_left = off_len_left - part_len_left;
									inside_part = false;																		
								}
								this.graphics.moveTo(x_tmp_2, y_tmp_2);								
							}
						}
					}
					prev_x = e.x;
					prev_y = e.y;
				}
			}
		}
		
		public override function get_colour(): Number {
			return this.props.get_colour('colour');
		}
	}
}