if (Garmin == undefined) var Garmin = {};

var player;
/**
 * This is essentially the public static void main of this page (or so I like to think)
 * Should be the first method called when the page loads.  
 */
Behaviour.addLoadEvent(function(){
	player = new ActivityPlayer("activityContent");
});

/**
 * @class ActivityPlayer
 * The one that's responsible for interaction between the swf's and the MapController
 * 
 * It creates and adds the swfs to the page as well as the JS based MapController.
 * Also responsible for adding the various controls to the map
 * 
 * After that it doesn't really do much besides hang out and give the various other JS 
 * controllers a place to talk to the map controller interface
 * 
 * @requires Prototype
 * @requires MapController
 * 
 * @constructor
 * @param {Element} container that holds all the swf's and the map element
 */
ActivityPlayer = function(container){}; // for jsdoc
var ActivityPlayer = Class.create();
ActivityPlayer.prototype = {
    initialize: function(container) {
		//js stuff
    	this.container = $(container);
        this.mapElement = null;
        this.mc = null;

		//display stuff
		this.offsetTop = 63;
		this.margin = 5;

		//flash stuff
        this.controlSWF = null;
        this.chartSWF = null;
        this.gridSWF = null;
        
        this.addControls();
        this.addMapControls();
    },

	/**
	 * Start playback of the track from the last position. If it's at the end of the track
	 * it will restart at the beginning 
	 */
	play: function() {
		this.controlSWF.play();
	},
	
	/**
	 * Stop playback of the track.  This will also reset the position to the start of the track
	 */
	stop: function() {
		this.controlSWF.stop();
	},
	
	/**
	 * Pause the playback of the track
	 * @param {Boolean} pause do we want to pause or resume playback of the track
	 */
	pause: function(pause) {
		this.controlSWF.pause(pause);
	},
	
	/**
	 * Tell the controller to move to the index passed in
	 * @ServerBound
	 * @param {int} index location we want to seek to
	 */
	seek: function(index) {
		this.controlSWF.seek(index);
	},
	
	/**
	 * Determine if we are currently playing the track information back or not
	 * @return {Boolean} if we are currently playing the track information back or not
	 */
	isPlaying: function() {
		return this.controlSWF.isPlaying()
	},

	/**
	 * Determine if we are currently playing the track information back or not
	 * @return {Boolean} if we are currently playing the track information back or not
	 */
	isPaused: function() {
		return this.controlSWF.isPaused()
	},

	/**
	 * If an activity is requested that doesn't have lat/lon data then there is no point in showing the map
	 * Remove the map, blow the chart up 
	 */
	removeMap: function() {
		Element.remove(this.mapElement);
		this.mc = null;
		this.mapElement = null;
		
		this.controlSWF.up('.swfContainer').setStyle({
			left: "60px",
			top: "75px"
			});
			
		this.chartSWF.up('.swfContainer').setStyle({
			width: "100%",
			right: "0px",
			top: "65px"
			});
		this.resizeChart();
		
		Event.stopObserving(window, "resize", this.resizeFunction);
		this.resizeFunction = this.resizeChart.bindAsEventListener(this);
		Event.observe(window, "resize", this.resizeFunction);
	},
	
	/**
	 * Need a function reference for the Event binder which is why this exists
	 * Resizes the chart to fill up the page if there is no mapping information 
	 */
	resizeChart: function() {
		this.resizeContainer(this.container, this.chartSWF.up('.swfContainer'));
	},
	
	/**
	 * Need a function reference for the Event binder which is why this exists
	 * Resizes the map when the screen size has changed.  Then tells the map itself that it's been 
	 * resized and should check itself
	 */
	resizeMap: function() {
		this.resizeContainer(this.container, this.mapElement);
		this.mc.map.checkResize();
	},

	/**
	 * Resizes the container and child passed in to be 100% - the offset and margin passed in
	 * @param {Element} container that should be the large container we are getting the size from
	 * @param {Element} child most likely a child of the container element.  Will grow to near size of the container
	 * @param {int} offsetTop size we'd like to keep the height away from.  Defaults to 65 
	 * @param {int} margin margin size to have.  default = 5
	 */
	resizeContainer: function(container, child, offsetTop, margin) {
		var offsetTop = offsetTop || this.offsetTop;
		var margin = margin || this.margin;
		
		for (var elem = container; elem; elem = elem.offsetParent) {
			offsetTop += elem.offsetTop;
		}

		child.setStyle({
			height: this.getWindowHeight() - offsetTop - margin + "px"
			});
	},
	
	/**
	 * Gets the height of the window.  Style points go to maps.google.com
	 * @return {int} the height of the whole window
	 */
	getWindowHeight: function() {
		if (window.self && self.innerHeight) {
			return self.innerHeight;
		}
		if (document.documentElement && document.documentElement.clientHeight) {
			return document.documentElement.clientHeight;
		}
		return 0;
	},

	/**
	 * And add the appropriate swf's to the page - as well as the JS based MapController
	 * Sets the resize function to the map and begins observing for resize events
	 * Useful to override in test environments
	 */
	addControls: function() {
		this.mapElement = this.container.down('.map');
        this.controlSWF = this.addFlashObject("./lib/button/ButtonControl.swf", "buttonPanel");
        this.chartSWF = this.addFlashObject("./lib/chart/ChartControl.swf", "chartPanel");
        this.gridSWF = this.addFlashObject("./lib/grid/DatagridControl.swf", "dataGridPanel");
        
        this.mc = new MapController(this.mapElement, this.controlSWF);

        this.resizeMap();
    	this.resizeFunction = this.resizeMap.bindAsEventListener(this);
        Event.observe(window, "resize", this.resizeFunction);
	},
	
    /**
     * Add whatever controls you want on the google map.  Must ensure that the map controller has already 
     * been intialized, and as long as you don't toy w/ the constructor it has been
     */
    addMapControls: function() {
        GEvent.bind(this.mc.map, "click", this, this.moveToClick);
        this.mc.map.enableDoubleClickZoom();
        this.mc.map.enableContinuousZoom();
        this.mc.map.addControl(new GMapTypeControl());
        this.mc.map.addControl(new GLargeMapControl());
        this.mc.map.addControl(new GScaleControl());
        this.mc.map.addControl(new GOverviewMapControl());
    },
	
    /**
     * Try and find the closest location to the location that was clicked.  If the click was near a trackpoint
     * we'll move the marker to that location
     * @param {GMarker} marker we don't care about markers at the moment
     * @param {GLatLng} click that represents where the user clicked
     */
    moveToClick: function(marker, clickPoint) {
        log('movin to click, marker: ' + marker + ", point: " + clickPoint);
        var CLOSE_ENOUGH = 300 * 13/this.mc.map.getZoom(); //close enough in meters for the map click
        var track = this.mc.polylines[0];

        var closestIndex = 0;
        var closestPoint = track.getVertex(0);
        var closestDistance = clickPoint.distanceFrom(closestPoint);
        
        for(var i=1; i<track.getVertexCount(); i+=5) {
	        var linePoint = track.getVertex(i);
	        var distance = clickPoint.distanceFrom(linePoint);
	
	        if(distance < closestDistance) {
	            closestIndex = i;
	            closestPoint = linePoint;
	            closestDistance = distance;
	        }
        }
        
        //if we're w/in range, narrow it down
        //TODO: refactor this 
        if(closestDistance < CLOSE_ENOUGH) {
            var baseIndex = closestIndex;
            for(var i=baseIndex-5; i<baseIndex+5; i++) {
	            var linePoint = track.getVertex(i);
                var distance = clickPoint.distanceFrom(linePoint);
                if(distance <= closestDistance) {
                    closestIndex = i;
                    closestPoint = linePoint;
                    closestDistance = distance;
                } 
            }
            this.seek( closestIndex );
        }
    },
	
    /**
     * Adds a flash object to the page using the SWFObject library.  SWF's are loaded after the pageComplete 
     * event so we don't have to "activate" them.  And swfobject handles writing out the appropriate tags for
     * each browser and inserts the params appropriately.
     * 
     * @param {String} source URL to the swf file.  Relative to /trail/player2/
     * @param {Element} id where we're putting the swf on the page - content of the element will be replaced
     * @return {Element} that represents the swf, ready to be stored as an object to have methods called upon
     */
    addFlashObject: function(source, id) {
        //create flash object
        var swfId = id + "Swf";
        
        var so = new SWFObject(source, swfId, "100%", "100%", "9.0.0");
    	so.addParam("wmode", "transparent");
    	so.addVariable("file", $F("file")); 
    	//so.addVariable("server", $F("server")); 
    	//so.addVariable("file_pk", $F("episodePkValues"));
    	
    	//if flash isn't installed, display the Get Flash picture instead
    	if( so.write(id) ){
    		//awesome
    	} else {
    		var img = document.createElement("img");
    		img.src = "/trail/site/images/get_flash.jpg";
    		img.alt = "Download the Free Flash Player now!";
    
    		var link = document.createElement("");
    		link.href = "http://www.macromedia.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash";
    		link.appendChild( img );
    		
    		this.container.replace(link);
    	}
    	return this.getElement(swfId);
    },

    /**
     * Apparently document.getElementById doesn't work for Flash objects, so we need this
     * function to get the controller
     * 
     * @param {String} id dom id of the swf you want to retrieve
     * @return {Element} the flash controller element
     */
    getElement: function(id) {
        return (navigator.appName.indexOf("Microsoft") != -1) ? window[id] : document[id];
    },
    
    /**
     * Lovely toString function - prints the map id and the controller id
     * @return {String}
     */
    toString: function() {
        return "ActivityPlayer.  Map: " + this.mapElement.id + " swf: " + this.controlSWF;
    }
};

/************************ Random Semi Useful Function *************************/
/**
 * Echo's what you passed in
 * @param {Object} item anything you want to be immediatly returned
 * @return {Object} the item you passed in
 */
function echo(item) {
	return item;
}