/* This MovieClip is a holder for the following. 
[1] The ScrollPane which contains the graph.
[2] A square 'dragger' that covers most of the ScrollPane (all apart from 
    scroll bars) which may be selected for startDrag and stopDrag commands.
[3] A small 'resizer' square so that user can resize the scrollPane. 
[4] A draggable top bar that goes above the ScrollPane. Contains controls.
*/	


import mx.containers.ScrollPane;
import Curve;
import Graph;

class GraphHolder extends MovieClip {
	
	//Width and height of the ScrollPane.
	public var nWidth:Number = 200;
	public var nHeight:Number = 200;
	
	//Actionscript uses pixels as units. We use following units, set to 20 pixels.
	public var xUnit:Number = 20;
	public var yUnitDist:Number = 20;
	public var yUnitVeloc:Number = 20;
	public var yUnitAccel:Number = 20;
	
	//Initial length of axes of graph in above units.
	public var xLow:Number = -50;
	public var xHigh:Number = 50;
	public var yLowDist:Number = -5;
	public var yLowVeloc:Number = -5;
	public var yLowAccel:Number = -5;
	public var yHighDist:Number = 10;
	public var yHighVeloc:Number = 10;
	public var yHighAccel:Number = 10;
	
	//True iff we show ticks on the axes.
	public var ticks:Boolean = true;
	
	//True iff we show numbers on the axes. MUST HAVE TICKS=true if markers=true;
	public var markers:Boolean = true;
	
	//True iff grid drawn through ticks. MUST HAVE TICKS=true if grid=true;
	public var grid:Boolean = true;
	
	//Separation of ticks (in units).
	public var xTickGap:Number = 1;
	public var yTickGapDist:Number = 1;
	public var yTickGapVeloc:Number = 1;
	public var yTickGapAccel:Number = 1;
	
	//Current time of the graphing unit. Measured in seconds.
	public var time:Number = 0;
	
	//Interval ID of the timing device.
	private var intervalID:Number = 0;
	
	//Determines whether we are currently timing or not.
	public var timerOn:Boolean = false;
	
	//At the moment Graph only plots ONE curve. Paramaters below
	//are for this one curve. 
	//Probably no  need for this -- just plot the lot.
	public var distance:Boolean = true;
	public var velocity:Boolean = true;
	public var speed:Boolean = false;
	public var acceleration:Boolean = true;
	
	//Configuration data for the lines that are plotted.
	public var curveAlpha:Number = 100;
	public var curveThickness:Number = 1;
	
	//Each Graph can support a number of Curves. Can specify how many.
	public var curve:Array;
	public var howManyCurves:Number = 1;
	
	//Visibility of the buttons.
	public var resizeHorizVis:Boolean = true;
	public var resizeVertVis:Boolean = true;
	public var minimiseVis:Boolean = true;
	public var playVis:Boolean = true;
	public var stopVis:Boolean = true;
	public var switcherVis:Boolean = true;
	public var clearBtnVis:Boolean = true;
	
	//Labels for the axes
	public var xAxisLabel:String = null;
	public var yAxisLabelDist:String = null;
	public var yAxisLabelVel:String = null;
	public var yAxisLabelAccel:String = null;
	
	//How often setInterval is called.
	public var millisecs:Number = 100;
	
	//True iff axes are to have labels.
	public var axesLabels:Boolean = true;
	
	//This is the function that gets plotted.
	public var input_fn1:Function = null;
	public var input_fn2:Function = null;
	public var thisObject:Object;
	/* Ian, the purpose of above is if want to call GraphHolder from another
	class. In which case we'd pass the instance of the class as thisObject
	and straight up you might think that we should then have thisObject.input_fn
	written in this class, but no, that would be wrong as input_fn not a function
	in whatever thisObject is. Instead use code: input_fn.call(thisObject).*/
	
	//Colours of the set-ups. Defaults are red/blue/green resp.
	public var distCol:Number = 6684672;
	public var velocCol:Number = 3355545;
	public var accelCol:Number = 39168;
	
	//This is on iff ScrollPane scrolls along with cursor.
	private var scrollerOn:Boolean = false;
	
	//This takes value 0 if user sees distance graph, 1 if veloc, 2 accel.
	private var distVelAccel:Number = 0;
	
	function GraphHolder() {
		curve = new Array(howManyCurves);
		//Create an array of the different Curves.
		for (var i:Number = 0; i!=howManyCurves; i++){
			curve[i] = new Curve();
			curve[i].showDist = distance;
			curve[i].showVeloc = velocity;
			curve[i].showSpeed = speed;
			curve[i].showAccel = acceleration;
		}
	
		//Hide the stop button.
		this["topBar"]["stop_btn"]._visible = false;
	}
	
	private function init():Void {
		
		attachDragger();
		attachResizers();
		attachTopBar();
		minimiseControl();
		timerControl();
		clearControl();
		toolTipControls();
		
		
		//Possibly make some of the buttons invisible.
		if (!resizeHorizVis) this["resizeHoriz"]._visible = false;
		if (!resizeVertVis) this["resizeVert"]._visible = false;
		if (!minimiseVis) this["minimise"]._visible = false;
		
		//Move whole thing across otherwise parts off screen.
		//this._x = 65;
		//this._y = 5;
		
	}
	
	//Bring in the dragging square
	private function attachDragger():Void {
		var dragger = attachMovie("dragger","dragger",this.getNextHighestDepth());

		dragger._width = nWidth + 60 - 18;
		dragger._height = nHeight - 18;
		dragger._x = this["graph_pane"]._x - 60;
		dragger._y = this["graph_pane"]._y;
		dragger._alpha = 0;

		dragger.onPress = function() { this._parent.startDrag(); }
		dragger.onRelease = function() { this._parent.stopDrag(); }
	}
		
	//Bring in the resizing square
	private function attachResizers():Void {
		var resizeHoriz = attachMovie("resizeHoriz","resizeHoriz",this.getNextHighestDepth());
		resizeHoriz._x = this["graph_pane"]._x + nWidth-18;
		resizeHoriz._y = this["graph_pane"]._y + nHeight-18;
		
		var resizeVert = attachMovie("resizeVert","resizeVert",this.getNextHighestDepth());
		resizeVert._x = this["graph_pane"]._x + nWidth-18;
		resizeVert._y = this["graph_pane"]._y + nHeight-18;
		
		
		
		resizeHoriz.isPressed = false;
		resizeHoriz.onPress = function() { this.isPressed = true;}
		resizeHoriz.onRollOver = function() { this._parent["horiz_tip"].show(300,true);}
		resizeHoriz.onRollOut = function() { this._parent["horiz_tip"].hide(300); }
		resizeHoriz.onRelease = function() { this.isPressed = false;this._parent["horiz_tip"].hide(300);}
		resizeHoriz.onReleaseOutside = function() { this.isPressed = false;this._parent["horiz_tip"].hide(300);}
		

		resizeVert.isPressed = false;
		resizeVert.onPress = function() { this.isPressed = true;}
		resizeVert.onRollOver = function() { this._parent["vert_tip"].show(300,true);}
		resizeVert.onRollOut = function() { this._parent["vert_tip"].hide(300); }
		resizeVert.onRelease = function() { this._parent["vert_tip"].hide(300);this.isPressed = false;}
		resizeVert.onReleaseOutside = function() { this._parent["vert_tip"].hide(300);this.isPressed = false;}


	}
	
	private function attachTopBar():Void {
		var topBar = attachMovie("topBar","topBar",this.getNextHighestDepth());
		topBar["strip"]._width = this["graph_pane"].width;
		this["topBar"]["minimise_btn"]._x = this["graph_pane"].width - 10;
		
		//Button that changes view.
		this["topBar"]["switcher_btn"].onPress = function() {
			this._parent._parent.switchView();
		}
	}
	
	//Function changes view of graph; dist, veloc or accel.
	public function switchView():Void {trace("switchview" + distVelAccel);
		var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"];
		if (distVelAccel==0) {
			sp["curveDist"]._visible = false;
			sp["curveVeloc"]._visible = true;
			sp["axesDist"]._visible = false;
			sp["axesVeloc"]._visible = true;
			distVelAccel=1;
		} else if (distVelAccel==1) {
			sp["curveDist"]._visible = true;
			sp["curveVeloc"]._visible = false;
			sp["axesDist"]._visible = true;
			sp["axesVeloc"]._visible = false;
			distVelAccel=0;
		} else if (distVelAccel==2) {
			sp["curveAccel"]._visible = false;
			sp["curveDist"]._visible = true;
			sp["axesAccel"]._visible = false;
			sp["axesDist"]._visible = true;
			distVelAccel=0;
		}
	}
	
	//The minimise button makes everything invisible.
	private function minimiseControl():Void {
		this["topBar"]["minimise_btn"].onPress = function() {
			var tp:MovieClip = this._parent._parent;
			tp["graph_pane"]._visible = (tp["graph_pane"]._visible) ? false : true;
			if (tp.resizeHorizVis)
				tp["resizeHoriz"]._visible = (tp["resizeHoriz"]._visible) ? false : true;
			if (tp.resizeVertVis)
				tp["resizeVert"]._visible = (tp["resizeVert"]._visible) ? false : true;
			tp["dragger"]._visible = (tp["dragger"]._visible) ? false : true;
			tp["graph"]._visible = (tp["graph"]._visible) ? false : true;
			tp["cp"]._visible = (tp["cp"]._visible) ? false : true;
		}
	}
	
	//Readjust size of graph. Parameter true iff configure in x-direction.
	public function configure(horizStretch:Boolean):Void {
		
		//Determine in which direction to stretch.
		if (horizStretch) {
			var m:Number = _xmouse-this["graph_pane"]._x;
			if (70<m && m <550) this["graph_pane"].setSize(m,this["graph_pane"].height);
		} else {
			var m:Number = _ymouse-this["graph_pane"]._y;
			if (70<m && m <550) this["graph_pane"].setSize(this["graph_pane"].width,m);
		}
		
		//Move resizer buttons.
		this["resizeHoriz"]._x = this["graph_pane"]._x + this["graph_pane"].width-18;
		this["resizeHoriz"]._y = this["graph_pane"]._y + this["graph_pane"].height-18;
		this["resizeVert"]._x = this["graph_pane"]._x + this["graph_pane"].width-18;
		this["resizeVert"]._y = this["graph_pane"]._y + this["graph_pane"].height-18;
		//Move dragger.
		this["dragger"]._width = this["graph_pane"].width + 60 - 18;
		this["dragger"]._height = this["graph_pane"].height - 18;
		//Top bar width
		this["topBar"]["strip"]._width = this["graph_pane"].width;
		//Minimise button.
		this["topBar"]["minimise_btn"]._x = this["graph_pane"].width - 10;
		//Move control panel.
		
		if (!timerOn) {
			this["graph_pane"].hPosition = -xLow*xUnit - Math.floor( (this["graph_pane"].width-18)/2 );
			//this["graph_pane"].vPosition = yHighDist*yUnitDist;// - Math.floor( (this["graph_pane"].height-18)/2 );
			
			//trace("tp= "+this["graph_pane"]);
		}
		
	}
	
	//When timing button pressed, either starts or stops counter.
	private function timerControl():Void {
		
		var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"];
		//This is the red marker that shows what time we're on.
		sp.attachMovie("timer_mc","timer_mc",this.getNextHighestDepth());
		
		this["topBar"]["play_btn"].onPress = function() {
			this._parent._parent.timer();		
			this._visible = false; 
			this._parent["stop_btn"]._visible = true; 
		}
		this["topBar"]["stop_btn"].onPress = function() {
			this._parent._parent.timer();
			this._visible = false;
			this._parent["play_btn"]._visible = true; 
		}
	
		
	}
	
	//This is the function to call from another program to start
	//or stop the grapher.
	public function timer():Void {
		
		//We scroll along with cursor iff width condition satisfied.
		scrollerOn = true;//(this["graph_pane"].width<(xHigh-xLow)*xUnit) ? true : false;
		
		//trace("in timer");
		//If timer is off, turn it on. If on, then turn off.
		if (timerOn) {
			clearInterval(intervalID);	
			timerOn = false;
		} else {
			beginInterval(); 
			timerOn = true;
		}
	}
	
	//This fn starts counter.
	private function beginInterval():Void {
		
	 	if (intervalID != null) {
					//trace("clearInterval");
					clearInterval(intervalID);
		 }
		//Called every 100 milliseconds. If change 100 then change fn counter().
		//trace("graphIDbefore = " + intervalID);
		intervalID = setInterval(this,"counter",millisecs);
		//trace("graphIDafter = " + intervalID);
		updateAfterEvent();
	}
	
	public function stopper():Void {
		clearInterval(intervalID);
	}
	
	//Adds on time increment. This fn gets called by a setInterval loop.
	private function counter():Void {
		//trace("graph interval");
		
		var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"]["timer_mc"];

		if (sp._x>xHigh*xUnit) {
			//trace("Reached end");
			clearInterval(intervalID);
		}
		
		//Plot a segment straightaway.
		//First parameter = index of Curve plotted.
		//Second parameter = distance measurement to be plotted.
		plotSegment(0,input_fn1.call(thisObject),input_fn2.call(thisObject));
		
		time+=millisecs/1000;
		sp._x = xUnit*time;
	}
	
	//Returns time.
	public function getCursor():Number {
		return time;
	}
	
	//Set timer at a particular time.
	public function setCursor(t:Number):Void {
		time = t;
		var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"]["timer_mc"];
		sp._x = xUnit*time;
	}
	
	//Clear button must completely clear everything.
	private function clearControl():Void {
		this["topBar"]["clear_btn"].onPress = function() {
			this._parent._parent.clearAll();
		}
	}
	
	//Completely clear graphs.
	public function clearAll():Void {
		//trace("Button is "+this["cp"]["clear_btn"]._visible);
		//CLEAR.
		time=0;
		timerOn = false;
		clearInterval(intervalID);
		var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"];
		sp["timer_mc"]._x = 0;
		sp["curveDist"].clear();
		sp["curveVeloc"].clear();
		sp["curveAccel"].clear();
		//Delete all existing curves.
		curve.splice(0,curve.length);
		//Create new empty curves as replacements. 
		for (var i:Number = 0; i!=howManyCurves; i++) {
			curve[i] = new Curve();
			curve[i].showDist = distance;
			curve[i].showVeloc = velocity;
			curve[i].showSpeed = speed;
			curve[i].showAccel = acceleration;
		}
		
		if (playVis) this["topBar"]["play_btn"]._visible = true;
		this["topBar"]["switcher_btn"]._visible = switcherVis;
		this["topBar"]["clear_btn"]._visible = clearBtnVis;
		this["topBar"]["stop_btn"]._visible = false;
		//trace("clearing");
		
		zeroHScroll();
	}
	
	function zeroHScroll():Void {
		this["graph_pane"].hPosition = 0;
	}
	
	//The first param. index is the index of the curve.
	//The measurement is the recorded distance.
	//I'm going to blank out a few bits that are not needed for Charlie's 
	//version, to speed things up.
	public function plotSegment(index:Number,measurement1:Number,measurement2:Number):Void {
		
		
		//Scroll graph along.
		if (scrollerOn)
		this["graph_pane"].hPosition = -100 -xLow*xUnit - Math.floor( (this["graph_pane"].width-18)/2 )+xUnit*time;
		//trace("hPosition = "+this["graph_pane"].hPosition);
		
		
		//trace("plotSeg: " + measurement1);
		
		//Add a new value to the curve distance measurements.
		var c = curve[index];
		var t = c.times.length;
		c.times[t] = time; 
		c.dist[t] = measurement1;
		c.veloc[t] = measurement2;
		
	
		
		//Only plot a segment after first run through.
		if (t>0) {			
			
			//Only plot the segment if the relevant line is turned on.
			//distance:0,velocity:1,speed:3,acceleration:2.
			//trace("c.dist[t-1]) = " + c.dist[t-1]);
			//trace("c.dist[t]) = " + c.dist[t]);
			var sp = this["graph_pane"]["spContentHolder"]["empty"]["graph"];
			if (c.showDist) sp.plotSegment(0,c.times[t-1],
										  c.dist[t-1],c.times[t],c.dist[t],c.distCol);
			
			//Only plot the segment if the relevant line is turned on.
			if (c.showVeloc) sp.plotSegment(1,c.times[t-1],
					c.veloc[t-1],c.times[t],c.veloc[t],c.velocCol);
			
			/*blank
			//Only plot the segment if the relevant line is turned on.
			if (c.showSpeed) {trace("whyeinhere"); sp.plotSegment(3,c.times[t-1],
					Math.abs(c.veloc[t-1]),c.times[t],Math.abs(c.veloc[t]),c.velocCol);
			}
			//Only plot the segment if the relevant line is turned on.
			if (c.showAccel) sp.plotSegment(2,c.times[t-1],
					c.accel[t-1],c.times[t],c.accel[t],c.accelCol);
			*/
		}
	}
	
	private function toolTipControls():Void {
		var t = this["topBar"];
		t["play_btn"].onRollOver = function() { t["play_tip"].show(300,true);}
		t["play_btn"].onRollOut = function() { t["play_tip"].hide(300); }
		t["play_btn"].onRelease = function() { t["play_tip"].hide(300); }
		t["play_btn"].onReleaseOutside = function() { t["play_tip"].hide(300); }
		
		t["stop_btn"].onRollOver = function() { t["stop_tip"].show(300,true);}
		t["stop_btn"].onRollOut = function() { t["stop_tip"].hide(300); }
		t["stop_btn"].onRelease = function() { t["stop_tip"].hide(300); }
		t["stop_btn"].onReleaseOutside = function() { t["stop_tip"].hide(300); }
		
		t["minimise_btn"].onRollOver = function() { t["minimise_tip"].show(300,true);}
		t["minimise_btn"].onRollOut = function() { t["minimise_tip"].hide(300); }
		t["minimise_btn"].onRelease = function() { t["minimise_tip"].hide(300); }
		t["minimise_btn"].onReleaseOutside = function() { t["minimise_tip"].hide(300); }
		
		t["clear_btn"].onRollOver = function() { t["clear_tip"].show(300,true);}
		t["clear_btn"].onRollOut = function() { t["clear_tip"].hide(300); }
		t["clear_btn"].onRelease = function() { t["clear_tip"].hide(300); }
		t["clear_btn"].onReleaseOutside = function() { t["clear_tip"].hide(300); }
		
		t["switcher_btn"].onRollOver = function() { t["switch_tip"].show(300,true);}
		t["switcher_btn"].onRollOut = function() { t["switch_tip"].hide(300); }
		t["switcher_btn"].onRelease = function() { t["switch_tip"].hide(300); }
		t["switcher_btn"].onReleaseOutside = function() { t["switch_tip"].hide(300); }
	}
	
}