/**
 * @class Sample
 * Sample class reprsents a sample point from a track(meter in the java world)
 * A sample contains an associative array of measurements, which can be retrieved with a 
 * @see #getMeasurement call passing in a string index.
 *   
 * @constructor 
 * Takes in an ActionScript LatLonEle object and converts to a Sample
 * we can use w/ methods in Javascript
 * 
 * @param {Object} asPoint is a LatLonEle that contains an associate array called measurements
 */
Sample = function(asPoint){};
var Sample = Class.create();
Sample.prototype = {
	initialize: function( asPoint ) {
	    this.measurements = asPoint.measurements;
	},
	
	/**
	 * Get a Measurement from this sample point
	 * If the measurement does not exist - return null
	 * 
	 * @param {String} context of the measurement we would like to get
	 * @return {Measurement} a measurement object (important to remember it's value is in measurementObject.value!)
	 * 	or null if the measurement doesn't exist
	 * @member Sample
	 */
	getMeasurement: function(context) {
		var meas = this.measurements[context];
		if(meas == undefined) {
		  meas = null;
		}
		return meas;
	},

	/**
	 * Determines if this sample point is valid for determining location
	 * 
	 * @return {Boolean} true if lat/lon exist, false otherwise
	 * @member Sample
	 */
    isValidLocation: function() {
        return ( (this.getLat() != "null") && (this.getLat() != null) && (this.getLat() != "") && (this.getLon() != "null") && (this.getLon() != null) && (this.getLon() != ""));
    },

	/**
	 * Returns the LatLon Object for whatever mapping system we will use
	 * Should be overridden in that js file
	 * 
	 * @return a LatLon object appropriate for the mapping system we're using
	 * @type MapBasedLatLon
	 * @member Sample
	 */
	getLatLon: function() {
		displayAlert("should be overridden in mapping file");
	},

	/**
	 * Shortcut for directly getting the lat value
	 * 
	 * @return the value of the latitude for this point
	 * @type Number
	 * @member Sample
	 */
	getLat: function() {
	    var meas = this.getMeasurement( "lat" );
	    if(meas == null) {
	    	return null;
	    } else {
	    	return meas.value;
	    }
	},
	/**
	 * Shortcut for directly getting the lon value
	 * 
	 * @return the value of the longitude for this point
	 * @type Number
	 * @member Sample
	 */
	 getLon: function() {
		var meas = this.getMeasurement( "lon" );
	    if(meas == null) {
	    	return null;
	    } else {
	    	return meas.value;
	    }	
	},
	
	/**
	 * @return {String}
	 */
	toString: function() {
		return "Sample Point: (" + this.getLat() + ", " +  this.getLon() + ")";
	}
};



/**
 * A class that just encapsulates an array of points.  
 * This will simplify the getting of the start/end points and ensuring we don't 
 * throw out of bounds exceptions on the arrays
 * 
 * @class Track
 */
var Track = Class.create();
Track.prototype = {
    /**
     * We will be passed an array of points from the AS object
     * The array will contain objects of points
     *  
     * @constructor
     * @member Track
     */
    initialize: function(trackPoints) {
        this.points = new Array();
        for(var i=0; i<trackPoints.length; i++) {
            this.points.push( new Sample( trackPoints[i] ) );
        }
    },
    
    /**
     * Find the nearest valid point to the index given
     * 
     * @param index is the index
     * @param incDirection is an int in the direction we'd like to look positive 
     * 	nums are forward, negative nums are backwards
     * 
     * @return the nearest point (possibly the index) that has a validLocation
     * @type Sample
     * @member Track
     */ 
    findNearestValidLocationPoint: function(index, incDirection) {
        if( this.getPoint( index ).isValidLocation() ) {
            return this.getPoint( index );
        } else if( index >= this.getLength() ) {
        	return this.findNearestValidLocationPoint(this.getLength()-1, -1);
        } else {
            return this.findNearestValidLocationPoint(index+incDirection, incDirection);
        }
    },

	/**
	 * Get the point specified on the track
	 * If the number is negative, get's the first
	 * If it's larger than possible, get's the last
	 * Otherwise it gets the number requested

	 * @param index is the point we want
	 * @return a sample that fits the pattern described above 
	 * @type Sample
     * @member Track
	 */
    getPoint: function(index) {
        index = Math.floor(index);
    
        if(index >= this.getLength()) {
            return this.getEnd();
        }
        if(index <= 0) {
            return this.getStart();
        }
            
        return this.points[index];
    },
    /**
     * Quick method to get the first point
     * @return the first point of this track
	 * @type Sample
     * @member Track
     */
    getStart: function() {
        return this.points[0];
    },
    /**
     * Quick method to get the last point
     * @return the last point of this track
	 * @type Sample
     * @member Track
     */
    getEnd: function() {
        return this.points[this.getLength()-1];
    },
    /**
     * Get the total length of the track
	 * @type Number
     * @member Track
     */
    getLength: function() {
        return this.points.length;
    },
    toString: function() {
        return "Track w/ " + this.getLength() + " points.";
    }
};


/**
 * When this class is created it hangs back and waits for both the Controller and the map to initialize
 * When they do, it then takes over the role of acting as an interface between the JS and the AS calls
 * 
 * There is a lot of mention about map specific stuff in here.  There is a huge and gaping difference in 
 * the Google/Yahoo/ESRI api, and this attempts to act as a generic interface between them.  Each "class" 
 * needs to extends this prototype and implement these methods.  In the act of doing this, they will all
 * use their own unique co-ordinate system, but will be getting this information from the @see Sample#getLatLon
 * listed above   
 * 
 * Other unique characteristics include polylines, bounds and zoom levels
 */
var MapController = Class.create();
MapController.prototype = {
    /**
     * Initializes the player with the map and controller 
     * @param mapString is the id of the map this is controlling
     * @param controllerString is the id of the Flash object 
     *  passing information to this object
     * @constructor
     * @member MapController
     */
    initialize: function(mapString, controllerString) {
        log("initialize should be over-ridden by child class!");
    },
    
    /**
     * Center the map on this location
     * @param lat is the latitude of the center point
     * @param lon is the longitude of the center point 
     * @member MapController
     */
    centerAndScale: function(lat, lon) {
        log("centerAndScale should be over-ridden by child class!");
    },
    
    /**
     * This is called when a track is added, and needs to be represented on the map
     *  @param points is an array of objects that contain Lat/Lon measurements.  Represented as
     * 		track = { 
     * 			{measurements: latitude:{value:38,context:"latitude"}, longitude:{value:39,context:"longitude"}}, 
     * 			{measurements: latitude:{value:38.5,context:"latitude"}, longitude:{value:39.5,context:"longitude"}
 	 * 		}
 	 * 		Each {measurements:} object represents a Sample point, and the complete object represents an array
 	 * @param color is the color we should painting the track and marker 
     * @member MapController
     */
    drawTrack: function(points, color) {
        log("drawTrack should be over-ridden by child class!");
    },
    
    /**
     * Recenter the map if it is found that the marker will be outside the bounds of the map
     * @member MapController
     */
    checkMoveMap: function() {
        log("checkMoveMap() should be over-ridden by child class!");
    },

    /**
     * Called to move the markers to the new location corresponding to this index
     * @param index should be an integer value corresponding to the index of the track
     * 	we should move the markers too 
     * @member MapController
     */
    moveMarkers: function(index, measurements) {
        log("moveMarkers should be over-ridden by child class!");
    },
    
    /**
     * Have the map attempt to find a sufficient zoom level.  The entire track should fit on the 
     * viewable area of the map without having to pan
     * @param points is an array of the implementing map's LatLon points (not Samples!) that correspond
     * 	to the track we want to fit on the map 
     * @member MapController
     */    
    findAZoomLevel: function(points) {
        log("findAZoomLevel should be over-ridden by child class!");
    },
    
    /**
     * Get the map to fit the bounds passed in to show on the map as well 
     * 	as center on the middle of the bounds
     * @param bounds is the map's implementation of a map boundary
     * @member MapController
     */
    setOnBounds: function(bounds) {
        log("setOnBounds should be over-ridden by child class!");
    },
    
    /**
     * Set's the map to the large mode.  Then re-zooms the map so the entire track will
     * fit within the screen
     * @member MapController
     */
    enterFullScreen: function() {
        log("enterFullScreen should be overridden by child class!");
    },
    
    /**
     * Set's the map to the small mode.  Then re-zooms the map so the entire track will
     * fit within the screen
     * @member MapController
     */
    enterPlayerMode: function() {
        log("enterPlayerMode should be overridden by child class!");
    },
    
	/**
	 * Check the new dimensions of the map, and determine the bounds of the tracks
	 * Then set the map to zoom to that bound level
	 * @member MapController
	 * @private
	 */    
    sizeAndSetOnBounds: function() {
        log("sizeAndSetOnBounds should be overridden by child class!");
    }
};


/**
 * Display an alert message sent from ActionScript
 * So very useful in testing/debugging, espicially since it's easy 
 * to comment out stuff if you just use this instead of alert
 * @param message is the message to display
 */
function log(message) {
    //alert(message);
}
