/* ---------------
// ZoomPane Component
// ---------------
//
// AUTHOR:  Sammy Joe Osborne
// DATE:    January 21, 2008
//
// DESCRIPTION: Creates a Pane with the desired content.  Content then becomes zoomable with provided
				or specified zoom-in/zoom-out buttons.  Content becomes draggable in Pane once zoomed in.
                All aspects can be handled in the parameters panel or set with actionscript.
				
				Listed below are the accesible properties:
				------------------------------------------
                
				content:MovieClip - get the MovieClip object currently serving as content in the ZoomPane
				contentPath:String - get/set the content of the ZoomPane using the desired objects ID path	
	            				
				zoomFactor:Number - get/set the zoom factor
				autoSize:String - get/set (values: "true" or "false").  If true, auto sizes the ZoomPane to fit the content
				
				ZoomInButton_Name:String - get/set the zoom-in button instance name if you dont want the default buttons
				zoomInButton:MovieClip - get the MovieClip representing the zoom-in button
				ZoomOutButton_Name:String - get/set the zoom-out button instance name if you dont want the default buttons
				zoomOutButton:MovieClip - get the MovieClip representing the zoom-out button
				
				ZoomInIcon:String  -  get/sets the identifier path of the zoom-in mouse icon
				
				


*/



//import UIComponent so I can access it's methods and stuff directly
import mx.core.UIComponent;
import mx.transitions.Tween;
import mx.transitions.easing.*;
import mx.utils.Delegate;

//Event metadata tag...I don't know its purpose, but it's in all the other components
[Event("change")]
class ZoomPane extends UIComponent
{
	//All Components must declare these to be proper components
	//in the components framework.  This must be some standard that you "should" follow.
	static var symbolName:String = "ZoomPane"; //so the name of the component...
	static var symbolOwner:Object = ZoomPane; //A reference to this class/object?
	var className:String = "ZoomPane";
	
	//to hold the components instance name
	//----Do I really need this, or does this act as a movieclip and have a _name var?
	private var myInstanceName:String;
	
	//Graphical elements of the ZoomPane
	private var borderFrame:MovieClip;
	private var contentMask_mc:MovieClip;
	private var content_mc:MovieClip;
	private var zoomPane_mc:MovieClip;
	private var btnZoomIn_mc:MovieClip;
	private var btnZoomOut_mc:MovieClip;
	private var zoomInIcon_mc:MovieClip;
	private var boundingBox_mc:MovieClip;
	var initializing:Boolean = true;
	
	//For the dragging marquee
	var marquee_mc:MovieClip;
	var xZoomPoint;
	var yZoomPoint;
	var validClick:Boolean = false;
	var validRelease:Boolean = false;
	
	//These are accessible through getters/setters to set the content movie clip and button instance names
	private var __contentPath:String = "";
	private var __btnZoomInName:String = "";
	private var __btnZoomOutName:String = "";
	private var __zoomInIconPath:String ="";
	private var __autoSize:Boolean = false;
	private var __zoomFactor:Number = 2.5;
	
	//Flags for the state of the ZoomPane.
	//Can be retrieved through getters
	//Will be set on events and cleared afterwards.
	private var __zoomedIn:Boolean = false;
	private var __panning:Boolean = false;
	
	//Sizing variables
	private var paneOriginalWidth;
	private var paneOriginalHeight;
	private var contentOriginalWidth;
	private var contentOriginalHeight;
	
	//Constructor
	function ZoomPane(){
	}
	
	//Initialization.  Required for all v2 components.  Must also call it's 
	//parents init() method with super.init(), which would be UIComponent.
	function init(Void):Void {
		super.init();
		boundingBox_mc._visible = false;
		boundingBox_mc._width = 0;
		boundingBox_mc._height = 0;
		
		setUserDefinedMovieClips();
		myInstanceName = this._name;
		
		//Call to addButtonCode() has to be done in createChildren(), 
		//since nothing has been added to the stage at this point
		
	}
	
	function setUserDefinedMovieClips():Void {
		//Attach the zoom in mouse icon.  If the user didn't specify one, 
		//or if it's invalid, use the default zoomInIcon clip provided
		zoomInIcon_mc = _parent.attachMovie(__zoomInIconPath, "zoomInIcon_mc", _parent.getNextHighestDepth());
		if(zoomInIcon_mc == undefined) {
			trace("Mouse Zoom-In icon not provded. Default icon is being used.");
			__zoomInIconPath = "defaultZoomInIcon";
			zoomInIcon_mc = _parent.attachMovie(__zoomInIconPath, "zoomInIcon_mc", _parent.getNextHighestDepth());
			
		}
		
		zoomInIcon_mc._visible = false;
		
		//Assigns the button movieclips, HOWEVER, in createChildren() these are checked to see if they've both
		//been specified.  If not, default buttons are added to the lower right corner.
		btnZoomIn_mc = _parent[__btnZoomInName];
		btnZoomOut_mc = _parent[__btnZoomOutName];
	}
	
	
	//Create children objects needed at start up.  The
	//createChildren() method is required for components
	//extending UIComponent.
	public function createChildren():Void {
		trace("createChildren");
		initializing = false;
		//add the content to the stage so we can get its width/height for autosizing purposes
		content_mc = createObject(__contentPath, "content", 10);
		if(__autoSize){
			setSize(content_mc._width, content_mc._height);
		}
		
		
		zoomPane_mc = createObject("borderFrame", "zoomPane_mc", 12); //This is just an attachMovie command!!! why macromedia, why...
		contentMask_mc = createObject("contentMask", "contentMask_mc", 11);
		
		establishContentSettings();
		/*
		//This creates an object in the content_mc with alpha set to zero so that all areas of the content_mc 
		//become clickable.  Otherwise, there might be gaps in the content_mc, so when the user tried to pan 
		//around, they'd have a hard time clicking on it.
		content_mc.createObject("contentMask", "alphaBackground", 30);
		content_mc.alphaBackground._width = width;
		content_mc.alphaBackground._height = height;
		content_mc.alphaBackground._alpha = 0;
		
		contentOriginalWidth = content_mc._width;
		contentOriginalHeight = content_mc._height;
		
		content_mc.setMask(contentMask_mc);*/
		
		//See comment in setUserDefinedMovieClips() method
		//This checks to see if both button instance names were provided and valid, and if not, use the default buttons
		if((btnZoomIn_mc == undefined)||(btnZoomOut_mc == undefined)) {
			trace("Zoom in/out buttons not provided or not found. Default icons are being used.");
			btnZoomIn_mc = createObject("btnDefaultZoomIn", "btnDefaultZoomIn", getNextHighestDepth());			
			btnZoomOut_mc = createObject("btnDefaultZoomOut", "btnDefaultZoomOut", getNextHighestDepth());
		}
		
		//must call this here instead of in init() because the neccessary movieclips aren't added until now
		addButtonCode();
		
		size();

	}
	
	private function establishContentSettings():Void {
		//This creates an object in the content_mc with alpha set to zero so that all areas of the content_mc 
		//become clickable.  Otherwise, there might be gaps in the content_mc, so when the user tried to pan 
		//around, they'd have a hard time clicking on it.
		content_mc.createObject("contentMask", "alphaBackground", 30);
		content_mc.alphaBackground._width = width;
		content_mc.alphaBackground._height = height;
		content_mc.alphaBackground._alpha = 0;
		
		//record the original width and height so it can return
		//back to its normal size after it's been zoomed
		contentOriginalWidth = content_mc._width;
		contentOriginalHeight = content_mc._height;
		
		content_mc.setMask(contentMask_mc);		
	}
	
	//draw() function.  Ok, so this redraws the component and is only called
	//from within an invalidate() call.  The alternative would be to put it in
	//the set() method for value, but that would be inefficient since there might be
	//multiple things that change in some components, causing a zillion draw()'s for 
	//each set() method instead of just one draw() with all the changes
	function draw():Void {
		super.draw();
		size();
	}
	
	//Invoked when the size changes
	//Also, resizes the children including the content
	function size():Void {
		super.size();
		drawFrame(zoomPane_mc, width, height); //draws the border for the zoompane
		contentMask_mc._width = width;
		contentMask_mc._height = height;		
		
		if(btnZoomIn_mc._name == "btnDefaultZoomIn"){
			relocateButtons();
		}
		
		//This causes a redraw, if neccessary
		invalidate();
	}
	
	//repositions the default buttons to the bottom right corner
	function relocateButtons():Void {
		btnZoomIn_mc._x = width - btnZoomIn_mc._width;
		btnZoomIn_mc._y = height - btnZoomIn_mc._height;
		
		btnZoomOut_mc._x = width - btnZoomIn_mc._width - btnZoomOut_mc._width;
		btnZoomOut_mc._y = height - btnZoomOut_mc._height;
	}
	
	
	function drawFrame(target, x2, y2){
		var strokeSize = 1;
		target.clear();
		target.lineStyle(strokeSize, 0x6F7777, 100, true, "normal", "square", "miter");
		target.moveTo(0, 0);
		target.lineTo(x2, 0);
		
		target.lineStyle(strokeSize, 0xD5DDDD, 100, true, "normal", "square", "miter");
		target.moveTo(x2, y2);
		target.lineTo(0, y2);
		
		target.lineStyle(strokeSize, 0x919999, 100, true, "normal", "square", "miter");
		target.moveTo(x2, 0);
		target.lineTo(x2, y2);

		target.lineStyle(strokeSize, 0x919999, 100, true, "normal", "square", "miter");
		target.moveTo(0, y2);
		target.lineTo(0, 0);
		
		//-----------------------		
		
		target.lineStyle(strokeSize, 0xD5DDDD, 100, true, "normal", "square", "miter");
		target.moveTo(x2-strokeSize, strokeSize);
		target.lineTo(x2-strokeSize, y2-strokeSize);
		
		target.lineStyle(strokeSize, 0xD5DDDD, 100, true, "normal", "square", "miter");
		target.moveTo(strokeSize, y2-strokeSize);
		target.lineTo(strokeSize, strokeSize);
		
		target.lineStyle(strokeSize, 0xC4CCCC, 100, true, "normal", "square", "miter");
		target.moveTo(strokeSize, strokeSize);
		target.lineTo(x2-strokeSize, strokeSize);
		
		target.lineStyle(strokeSize, 0xEEEEEE, 100, true, "normal", "square", "miter");
		target.moveTo(x2-strokeSize, y2-strokeSize);
		target.lineTo(strokeSize, y2-strokeSize);
	}
	
	//ok, the getter/setters for contentPath, autoSize, btnZoomInName and btnZoomOutName.
	//It Forces a call to invalidate() to redraw everything when the value changes.
	//The metatags are for the property inspector and binds the 
	//values the user types to this value, or something like that?
	[Bindable("writeonly")]
	[Inspectable(defaultValue="")]
	function set contentPath(path:String) {
		trace("set contentPath");
		if(!initializing)
		{
				content_mc = createObject(path, "content", 10);
				//This sets the mask and alpha background for the content
				//and also records the content's original width/height
				establishContentSettings();
		}
			
		__contentPath = path;
		invalidate();
	}
	
	function get contentPath():String {
		return __contentPath;
	}
	
	function get content():MovieClip {
		return content_mc;
	}
	
	[Bindable]
	[ChangeEvent("change")]
	[Inspectable(defaultValue="default")]
	function set ZoomInButton_Name(instanceName:String) {
		__btnZoomInName = instanceName;
		invalidate();
	}
	
	function get ZoomInButton_Name():String {
		return __btnZoomInName;
	}
	
	function get zoomInButton():MovieClip {
		return btnZoomIn_mc;
	}
	
	
	[Bindable]
	[ChangeEvent("change")]
	[Inspectable(defaultValue="default")]
	function set ZoomOutButton_Name(instanceName:String) {
		__btnZoomOutName = instanceName;
		invalidate();
	}
	
	function get ZoomOutButton_Name():String {
		return __btnZoomOutName;
	}
	
	function get zoomOutButton():MovieClip {
		return btnZoomOut_mc;
	}
	
	[Bindable]
	[ChangeEvent("change")]
	[Inspectable(defaultValue="default")]
	function set ZoomInIcon(path:String) {
		if(path = "default") path = "defaultZoomInIcon";
		__zoomInIconPath = path;
		invalidate();
	}
	
	function get ZoomInIcon():String {
		return __zoomInIconPath;
	}
	
	function get zoomInIcon():MovieClip {
		return zoomInIcon_mc;
	}
	
	[Bindable]
	[ChangeEvent("change")]
	[Inspectable(enumeration="false, true", defaultValue="false")]
	[Inspectable(defaultValue=false)]
	function set autoSize(val:String) {
		if(val == "true")
			__autoSize = true;
		if(val == "false")
			__autoSize = false;
		
		invalidate();
	}
	
	function get autoSize():Boolean {
		return __autoSize;
	}
	
	[Bindable]
	[ChangeEvent("change")]
	[Inspectable(defaultValue=3)]
	function set zoomFactor(val:Number) {
		__zoomFactor = val;
		invalidate();
	}
	
	function get zoomFactor():Number {
		return __zoomFactor;
	}
	
	
	
	
	
	
	
	
	//-------------------------  Gets pretty messy from here on  ----------------------------
	//----------------- This is all the zoom in button code, and Senocular's Marquee Code (thanks!) ----------------------
	function addButtonCode():Void {
		//Delegate keeps it in scope.  Otherwise the buttons have no access to the button movieclips
		//or any of the component variables for that matter
		btnZoomIn_mc.onRelease = Delegate.create(this, zoomInButtonCode); //this fixes the scope issue
		btnZoomOut_mc.onRelease = Delegate.create(this, zoomOutButtonCode);
	}
	
	function zoomInButtonCode():Void {			
			
			Mouse.hide();
			btnZoomIn_mc.enabled = false;
			btnZoomIn_mc._alpha = 20;
			
			
			zoomInIcon_mc._x = _root._xmouse;
			zoomInIcon_mc._y = _root._ymouse;
			zoomInIcon_mc.startDrag();
			zoomInIcon_mc._visible = true;
			
			//turn on content's zooming ability
			//Delegate keeps it in scope
			content_mc.onRelease = Delegate.create(this, zoomIn);				
			
			//----------------------------------------
			var marquee_mc:MovieClip;
			if(_root.marquee_mc._name == null){
				content_mc.createEmptyMovieClip("marquee_mc", 1);
			}
			marquee_mc = content_mc.marquee_mc;
			var antsbmp = flash.display.BitmapData.loadBitmap("ants");
			// define a matrix to control the shifting of
			// the ants pattern when its drawn
			var shift = new flash.geom.Matrix();
			var contentClickLoc;	// retains location mouse is clicked
			var contentReleaseLoc;	// retains location mouse is released
			var currLoc; 
				
			// when user presses mouse, start drawing the marquee if it's within the zoomPane
			// and we're not already zoomed in
			onMouseDown = function(){
				var hit_X = _parent._xmouse;
				var hit_Y = _parent._ymouse;
				if((this.hitTest(hit_X, hit_Y, false))&&(!__zoomedIn)){
					validClick = true;
					marquee_mc.clear();
					marquee_mc._visible = true;
					// set mouse locations
					// nothing for release yet
					contentReleaseLoc = null;
					// for click make a point based on the mouse location
					contentClickLoc = new flash.geom.Point(content_mc._xmouse, content_mc._ymouse);
				}
			}
			
			// when user releases the mouse, stop drawing the marquee
			onMouseUp = function(){
				//make the marquee disapear for now
				marquee_mc._visible = false;
				//if the user didn't click within the zoomPane, exit the function
				if(!validClick) return;
				
				// set release to a point based on the mouse location
				contentReleaseLoc = new flash.geom.Point(content_mc._xmouse, content_mc._ymouse);
				
				//make sure release point is in the bounds of the content clip, and if not set it to be
				if(contentReleaseLoc.x > content_mc._width){
					contentReleaseLoc.x = content_mc._width;
				}
				if(contentReleaseLoc.x < 0){
					contentClickLoc.x = 0;
				}
				if(contentReleaseLoc.y > content_mc._height){
					contentReleaseLoc.y = content_mc._height;
				}
				if(contentReleaseLoc.y < 0){
					contentReleaseLoc.y = 0;
				}
				
				xZoomPoint = (contentClickLoc.x + contentReleaseLoc.x)/2;
				yZoomPoint = (contentClickLoc.y + contentReleaseLoc.y)/2;
				//trace(xZoomPoint + ", " + yZoomPoint);
				
				//if(!this.hitTest(content_mc._xmouse, content_mc._ymouse, false)){
					//delete onMouseDown;
					//delete onMouseUp;
				//}				
				
				validClick = false;
				
				
			}
			
			// constantly update the marquee
			onEnterFrame = function(){
				// if the user hasnt clicked the mouse
				// to create a marquee, exit the function
				if (!contentClickLoc) return;
				
				// set currloc, the location the marquee will
				// be drawn to, to either releaseloc if it isnt null
				// or a point that is the location of the mouse - that
				// means the user is currently drawing the marquee
				currLoc = (contentReleaseLoc) ? contentReleaseLoc : new flash.geom.Point(content_mc._xmouse, content_mc._ymouse);
				
				// shift the matrix used for the ants down by 
				// a value of one - this makes them march
				shift.translate(0, 1);
				
				// the marquee is dynamically drawn into marquee_mc
				// with the ants as a bitmap fill
				// clear marquee_mc each frame to update
				marquee_mc.clear();
				// start a bitmap fill with the ants
				// using the shift matrix to offset their position
				marquee_mc.beginBitmapFill(antsbmp, shift);
				// draw two squares to make a 1 pix thick hollow square
				// that will contain the marquee. Draw around
				// clickloc and currloc
				drawSquare(marquee_mc, contentClickLoc.x, contentClickLoc.y, currLoc.x, currLoc.y);
				drawSquare(marquee_mc, contentClickLoc.x+1, contentClickLoc.y+1, currLoc.x-1, currLoc.y-1);
				// end the fill
				marquee_mc.endFill();
			}
			
			// method for drawing a square in target
			function drawSquare(target, x1, y1, x2, y2){
				target.moveTo(x1, y1);
				target.lineTo(x2, y1);
				target.lineTo(x2, y2);
				target.lineTo(x1, y2);
				target.lineTo(x1, y1);
			}
	}
	
	
	function zoomOutButtonCode():Void {	
		//set state to zoomed out
		__zoomedIn = false;		
		
		delete content_mc.onPress;
		delete content_mc.onRelease;
		delete content_mc.onReleaseOutside;
		delete onEnterFrame;
		
		//change size back to original size
		new Tween(content_mc, "_width", Regular.easeInOut, content_mc._width, contentOriginalWidth, 1, true);
		new Tween(content_mc, "_height", Regular.easeInOut, content_mc._height, contentOriginalHeight, 1, true);
		
		//move back to original location (0, 0)
		new Tween(content_mc, "_x", Regular.easeInOut, content_mc._x, 0, 1, true);
		new Tween(content_mc, "_y", Regular.easeInOut, content_mc._y, 0, 1, true);
		
		//btnZoomOut_mc._alpha = 20;
		btnZoomOut_mc.enabled = false;
		
		btnZoomIn_mc._alpha = 100;
		btnZoomIn_mc.enabled = true;
	}
	
	
	
	
	//Code to actually make the content scale up and appear to be "zooming in"
	function zoomIn():Void {
		//set state to zoomed in
		__zoomedIn = true;
		
		//change mouse icon back to normal
		zoomInIcon_mc._visible = false;
		Mouse.show();	
		
		//set vars for where it should move / resize to
		var midX = width/2; //midpoint of the zoomPane
		var midY = height/2; //midpoint of the zoomPane
		
		// Center the content based on where the user clicked.
		// It moves it to the center point as it would exist
		// within the scale factor
		var newX = midX - xZoomPoint*__zoomFactor;
		var newY = midY - yZoomPoint*__zoomFactor;
		
		
		
		//scale content (to zoom in)
		new Tween(content_mc, "_xscale", Regular.easeInOut, content_mc._xscale, __zoomFactor*100, 1, true);
		new Tween(content_mc, "_yscale", Regular.easeInOut, content_mc._yscale, __zoomFactor*100, 1, true);
		
		//move the content to where they clicked
		new Tween(content_mc, "_x", Regular.easeInOut, content._x, newX, 1, true);
		new Tween(content_mc, "_y", Regular.easeInOut, content._y, newY, 1, true);
		
		//make the content dragable once zoomed in
		content_mc.onPress = function(){
			this.startDrag();
			
			//set state to panning
			__panning = true;
		}
		content_mc.onRelease = function(){
			this.stopDrag();
			
			//set state to not panning
			__panning = false;
		}
		content_mc.onReleaseOutside = function() {
			this.stopDrag();
			
			//set state to not panning
			__panning = false;
		}
	
		//allow zoom out functionality
		btnZoomOut_mc.enabled = true;
		btnZoomOut_mc._alpha = 100;
		
		//delete the zoom in functionality
		delete this.onRelease;
	}

	
}