/**
 * Extend the Sample prototype to work in Google maps
 * @extends Sample
 */
Object.extend(Sample.prototype, {
    getLatLon: function() {
        return new GLatLng(this.getLat(), this.getLon());
    }
});

/**
 * Google implmentation of the MapController.  
 * 
 * @extends MapController
 */
Object.extend (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, usePositionMarker) {
        this.mapElement = $(mapString);
        this.controller = this.getElement(controllerString);
        this.usePositionMarker = true;
        
        this.polylines = new Array();
        this.markers = new Array();
        this.tracks = new Array();
        this.markerIndex = 0;

        this.timeToCheck = false;
        this.map = new GMap2( $(mapString) );
        new GKeyboardHandler(this.map);
        
        window.onUnload = "GUnload()";
    },

    centerAndScale: function(lat, lon, scale) {
        if(scale == null) scale = 13;
        this.map.setCenter(new GLatLng(lat, lon), scale);
    },
    
    /**
     * @see MapController#drawTrack
     * 
     * Problem is that Google Maps dies when you hit near 500 points, so we have to
     * ensure that we create fewer than that for the track.
     * 
     * Also creates the point arrays that define the track.  These arrays will be longer
     * than the drawn ones because they need to be more precise.
     */
    drawTrack: function(points, color) {
        var track = new Track(points);
        this.addTrack(track, color)
    },
    
    /**
     * this.usePositionMarker determines if the position marker should be placed
     * when a track is added.  if true, then it is added, false than it is not
     */    
    addTrack: function(track, color) {
        this.tracks.push( track );
        this.addMarker( track );

        //create a smaller version of the whole track
        //create 300 points or so ...
        var drawAt = Math.ceil(track.getLength()/300);
        var drawnPoints = new Array();

        //skip through most of them
        for(var i=0; i<track.getLength(); i+=drawAt) {
            try {
                drawnPoints.push( track.findNearestValidLocationPoint(i, -1).getLatLon() );
            } catch( e ) {
                log(e.message);
            }
        }
        try {
            drawnPoints.push( track.findNearestValidLocationPoint(track.getLength()-1,-1).getLatLon() );
        } catch( e ) {
            log(e.message);
        }
        
        //draw the new smaller version
        var polyline = new GPolyline(drawnPoints, "#ff0000", 2, .8)
        this.polylines.push( polyline );
        this.map.addOverlay( polyline );
        this.bounds = this.findAZoomLevel(drawnPoints);
        this.setOnBounds( this.bounds );
    },
 
    findAZoomLevel: function(points) {
        var bounds = new GLatLngBounds(points[0], points[0]);
        
        for(var i=1; i<points.length-1; i+=3) {
            bounds.extend(points[i]);
        }
        
        return bounds;
    },
    
    /**
     * 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() {
        this.mapElement.className = "fullScreen";
        this.sizeAndSetOnBounds();
    },
    
    /**
     * 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() {
        this.mapElement.className = "playerMode";
        this.sizeAndSetOnBounds();
    },
    
	/**
	 * 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() {
        this.map.checkResize();
        this.setOnBounds( this.bounds );
    },
    
    setOnBounds: function(bounds) {
        this.map.setCenter( this.bounds.getCenter(), this.map.getBoundsZoomLevel(this.bounds) );
    },
    
    addClickToMove: function() {
        GEvent.bind(this.map, "click", this, this.moveToClick);
    },
    
    moveToClick: function(marker, clickPoint) {
        log('movin to click, marker: ' + marker + ", point: " + clickPoint);
        var CLOSE_ENOUGH = 300 * 13/this.map.getZoom(); //close enough in meters for the map click
        var track = this.tracks[0];

        var closestIndex = 0;
        var closestPoint = track.getStart().getLatLon();
        var closestDistance = clickPoint.distanceFrom(closestPoint);
        
        for(var i=1; i<track.getLength(); i+=5) {
            try {
                var linePoint = track.findNearestValidLocationPoint(i,1).getLatLon();
                var distance = clickPoint.distanceFrom(linePoint);
    
                if(distance < closestDistance) {
                    closestIndex = i;
                    closestPoint = linePoint;
                    closestDistance = distance;
                }
            } catch(e) {
                log(e.message);
            }
        }
        
        //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++) {
                try {
                    var linePoint = track.getPoint(i);
                    if(linePoint.isValidLocation()) {
                        var distance = clickPoint.distanceFrom(linePoint.getLatLon());
                        if(distance <= closestDistance) {
                            closestIndex = i;
                            closestPoint = linePoint.getLatLon();
                            closestDistance = distance;
                        } 
                    }
                } catch(e) {
                    log(e.message);
                }
            }
            this.controller.seek( closestIndex );
        }
    },
    
    //TODO: only handles the first marker at the moment
    addMarker: function(track) {
        log('usePositionMarker: ' + this.usePositionMarker);
        if(this.usePositionMarker) {
            this.addMarkerWithIcon( track.getStart(), MBIcon.getRedIcon() );
        } 
    },

    /**
     * Adds a marker to the point and icon specified
     */
    addMarkerWithIcon: function(point, icon) {
        log(point + icon);
        var gMarker = new GMarker( point.getLatLon(), icon );
        this.markers.push( gMarker );
        this.map.addOverlay( gMarker );
    },

    /**
     * Adds a marker to the point and icon specified
     */
    addStartFinishMarkers: function(track) {
        this.addMarkerWithIcon(track.getStart(), MBIcon.getGreenIcon());
        this.addMarkerWithIcon(track.getEnd(), MBIcon.getRedIcon());
    },
    
    //TODO: moves all the markers, and stores the index of the current location
    moveMarkers: function(index) {
        for(var i=0; i<this.tracks.length; i++) {
            this.markers[i].setPoint( this.tracks[i].findNearestValidLocationPoint(index,1).getLatLon() );
        }
        this.markerIndex = index;
        if( $("panMapOnMarkerMove").checked && this.timeToCheck )
            this.checkMoveMap(this.markerIndex);
            
        this.timeToCheck = !this.timeToCheck;
    },
    
    //TODO: only handles checking the first marker
    //TODO: plan ahead and move before we get off the map
    checkMoveMap: function(currentIndex) {
        var point = this.tracks[0].findNearestValidLocationPoint( currentIndex+5,-1 ).getLatLon();
        var bounds = this.map.getBounds();
    
        //if it doesn't have the point, pan the map    
        if( !bounds.contains(point) ) {
            this.map.panTo( point );
        }
    },

    /**
     * Allows a user to "fix" a section of their data
     * Don't feel like interfacing w/ Flash to do this - in which case we could hopefully 
     * seperate this from the activity player
     *
     * It'd probably be real confusing to try and fix more than one track at a time, so i imagine we'll have a
     * fix/clean/modify/whatever button for each track, and then we will be able to figure out which one to mess
     * with through there
     */
    beginSectionFixing: function( trackNumber ) {
        $("notes").innerHTML = "You are fixing the track!  Each of the next 5 points will be identified by a marker, drag them to set new locations, then click finish fixing";
        var fixer = new MapFixer( this.markerIndex, this.tracks[trackNumber], this );
    },
    
    /**
     * End the fixing of the section, and transmit the data back to a Struts Action
     * Wait for a response (none of that part is implemented, just the front end stuff, and 
     * redraw the map w/ the new look
     */
    endSectionFixing: function() {
        $("notes").innerHTML = "Fixing has been finished.  If this was for real, we'd send an AJAX call to mama bear w/ the points.  She'd figure out the new information for the points (speed/elevation/other) we're just giving out lat/lon.  she should return an array with the new points and their information";
    },
    
    toString: function() {
        return "Google Based Map Controller, managing " + this.tracks.length + " track(s)";
    }
});

var MBIcon = {
    getRedIcon: function() {
        var icon = new GIcon();
        icon.image = "/trail/site/images/marker_red.png";
        return MBIcon._applyShadowAndStuff(icon);
    },
    
    getGreenIcon: function() {
        var icon = new GIcon();
        icon.image = "/trail/site/images/marker_green.png";
        return MBIcon._applyShadowAndStuff(icon);
    },
    
    getBaseIcon: function() {
    	var baseIcon = new GIcon();
		baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
		baseIcon.iconSize = new GSize(20, 34);
		baseIcon.shadowSize = new GSize(37, 34);
		baseIcon.iconAnchor = new GPoint(9, 34);
		baseIcon.infoWindowAnchor = new GPoint(9, 2);
		baseIcon.infoShadowAnchor = new GPoint(18, 25);
        return baseIcon;
    },
    
    _applyShadowAndStuff: function(icon) {
        icon.iconSize = new GSize(12, 20);
        icon.shadow = "/trail/site/images/marker_shadow.png";
        icon.shadowSize = new GSize(22, 20);
        icon.iconAnchor = new GPoint(6, 20);
        icon.infoWindowAnchor = new GPoint(5, 1);
        return icon;
    }
}

/**
 * Displays the logs in Google's neat logging way.  Should be commented out when not in dev
 */
function log(message) {
    //GLog.write(message);
}
