/**
 * This code is based on the samples that are part of the Garmin Communicator API.
 * See http://developer.garmin.com/web-device/garmin-communicator-plugin/
 * 
 * The original code was released under the Apache license - see below for details.
 * I'm no lawyer so I'm not 100% sure what that means, but please feel free to reuse this code
 * as you wish under the terms of the original license.
 *
 * ------------------------------------------------------------------------
 * Licensed under the Apache License, Version 2.0 (the 'License')
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ------------------------------------------------------------------------
 * 
 * @fileoverview GarminDeviceControlWindowsLive Demonstrates Garmin.DeviceControl.
 * @author John Pollard yeltzland.at.hotmail.com
 * @version 1.0
 */

var GarminDeviceControlWindowsLive = Class.create();
GarminDeviceControlWindowsLive.prototype = {

	initialize: function(statusDiv, mapId, keysArray) {        
        this.status = $(statusDiv);
        this.mc = new Garmin.WindowsLiveMapController(mapId);
        this.factory = null;
        this.keys = keysArray;
        
        this.findDevicesButton = $("findDevicesButton");
        this.cancelFindDevicesButton = $("cancelFindDevicesButton");
        this.deviceSelect = $("deviceSelect");

        this.readDataButton = $("readDataButton");
        this.cancelReadDataButton = $("cancelReadDataButton");
        this.readTracksText = $("readTracksText");
        this.readTracksSelect = $("readTracksSelect");

		this.progressBar = $("progressBar");
		this.progressBarDisplay = $("progressBarDisplay");
		
		this.lastKnownLocationText = $("lastKnownLocationText");
						
		this.garminController = null;
		this.intializeController();
				
		if(this.garminController && this.garminController.isPluginInitialized()) {
	        this.findDevicesButton.disabled = false;
	        this.findDevicesButton.onclick = function() {
	        	this.findDevicesButton.disabled = true;
	        	this.cancelFindDevicesButton.disabled = false;
	        	this.garminController.findDevices();
	        }.bind(this)
		}		
	},
	
	intializeController: function() {
		try {
			this.garminController = new Garmin.DeviceControl();
			this.garminController.register(this);
			
			if(this.garminController.unlock(this.keys)) {
	        	this.setStatus("Plug-in initialized.  Use the 'Find' button to get started.");
			} else {
	        	this.setStatus("The plug-in was not unlocked successfully.");
	        	this.garminController = null;
			}
		} catch (e) { this.handleException(e); }
	},

	showProgressBar: function() {
		Element.show(this.progressBar);
	},

	hideProgressBar: function() {
		Element.hide(this.progressBar);
	},

	updateProgressBar: function(value) {
		if (value) {
			var percent = (value <= 100) ? value : 100;
	    	this.progressBarDisplay.style.width = percent + "%";
		}
	},

    onStartFindDevices: function(json) {
        this.setStatus("Looking for connected Garmin devices...");
    },

    onFinishFindDevices: function(json) {
    	try {
	       	this.findDevicesButton.disabled = false;
	       	this.cancelFindDevicesButton.disabled = true;
	
	        if(json.controller.numDevices > 0) {
	            var devices = json.controller.getDevices();
	            this.setStatus("Found " + devices.length + " devices - use the 'Read' button to get the data from the selected device");
	
				this.listDevices(devices);
				
		        this.cancelReadDataButton.onclick = function() {
		        	this.readDataButton.disabled = false;
		        	this.cancelReadDataButton.disabled = true;
		        	this.writeDataButton.disabled = false;
		        	this.hideProgressBar();
		        	this.garminController.cancelReadFromDevice();
		        }.bind(this)
				
		        this.readDataButton.disabled = false;
		        this.readDataButton.onclick = function() {	
		        	this.activities = null;
			    	this.readTracksSelect.length = 0;	
					this.mc.clearMap();
		        	this.readDataButton.disabled = true;
		        	this.cancelReadDataButton.disabled = false;
		        	this.showProgressBar();
		        	
                    this.garminController.readFromDevice();
		        	
		       	}.bind(this)
		      		        		        		        
	        } else {
				this.setStatus("No devices found.");
				this._clearHtmlSelect(this.deviceSelect);
				this.deviceSelect.disabled = true;
	        }
    	} catch (e) { this.handleException(e); }
    },
    
	onCancelFindDevices: function(json) {
    	this.setStatus("Find cancelled");
    },

	listDevices: function(devices) {
		this._clearHtmlSelect(this.deviceSelect);
		for( var i=0; i < devices.length; i++ ) {
           	this.deviceSelect.options[i] = new Option(devices[i].getDisplayName(),devices[i].getNumber());
           	if(devices[i].getNumber() == this.garminController.deviceNumber) {
           		this.deviceSelect.selectedIndex = i;
           	}
		}
   		this.deviceSelect.selectedIndex = 0;
		this.deviceSelect.onchange = function() {
			var device = this.garminController.getDevices()[this.deviceSelect.value];
			this.garminController.setDeviceNumber(this.deviceSelect.value);
		}.bind(this)
		this.deviceSelect.disabled = false;
	},


    onProgressReadFromDevice: function(json) {
	  	this.updateProgressBar(json.progress.getPercentage());
    	this.setStatus(json.progress);
    },
    
	onCancelReadFromDevice: function(json) {
    	this.setStatus("Read cancelled");
    },

    onFinishReadFromDevice: function(json) {
    	try {
		    this.setStatus("Processing retrieved data...");
	       	this.readDataButton.disabled = false;
	       	this.cancelReadDataButton.disabled = true;
	       	this.hideProgressBar();
	    	
	    	// Factory setting
	    	this.factory = Garmin.GpxActivityFactory;
	    	
			// parse the data into activities if possible
			if (this.factory != null) {
				
				// Convert the data obtained from the device into activities.
				// If we're starting a new read session, start a new activities array
				if( this.activities == null) {
					this.activities = new Array();
				}
				
				// Populate this.activities
                this.activities = this.factory.parseDocument(json.controller.gpsData);
			}
			
    		// List the activities (and display on Map)
			if( this.activities != null) {
	    		this.setStatus("Listing activities...");
	    		var summary = this._listActivities(this.activities);
	    		this.setStatus( new Template("#{tracks} tracks found").evaluate(summary) );
	    	    	    	    
	    	    // Find the last known point
	    	    this._findLastKnownPoint(this.activities);

			} else {
				this.setStatus("Finished retrieving data.");
			} 
	    	
    	} catch (e) { this.handleException(e); }
    },
	
	/** List activities and display on Windows Live Map when appropriate.
	 */
   	_listActivities: function(activities) {
		var numOfTracks = 0;
		
		// clear existing entries
		this._clearHtmlSelect(this.readTracksSelect);
		
		// loop through each activity
		for (var i = 0; i < activities.length; i++) {
			var activity = activities[i];
			var series = activity.getSeries();
			
			// loop through each series in the activity
			for (var j = 0; j < series.length; j++) {
				var curSeries = series[j];		
				
				switch(curSeries.getSeriesType()) {
					case Garmin.Series.TYPES.history:
						// activity contains a series of type history, list the track
						this._listTrack(activity, curSeries, i, j);
						numOfTracks++;
						break;
				}	
			}
		}
		
		if(numOfTracks > 0) {
			this.readTracksSelect.disabled = false;
			this.readTracksSelect.selectedIndex = numOfTracks - 1;
			this.displayTrack(this.readTracksSelect.options[this.readTracksSelect.selectedIndex].value);			
			this.readTracksSelect.onchange = function() {
				this.displayTrack(this.readTracksSelect.options[this.readTracksSelect.selectedIndex].value);
			}.bind(this);
		} else {
			this.readTracksSelect.disabled = true;
		}
		
		return {tracks: numOfTracks};
	},


    /** Load track name into select UI component.
     * @private
     */    
	_listTrack: function(activity, series, activityIndex, seriesIndex) {
		var startDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.startTime).getValue();
		var endDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.endTime).getValue();
		var trackName = startDate.getDateString() + " (Duration: " + startDate.getDurationTo(endDate) + ")";
		this.readTracksSelect.options[this.readTracksSelect.length] = new Option(trackName, activityIndex + "," + seriesIndex);
	},
    
    /** Draws a simple line on the map using the Garmin.MapController.
     * @param {Select} index - value of select widget. 
     */
    displayTrack: function(index) {
    	index = index.split(",", 2);
    	var activity = this.activities[parseInt(index[0])];
    	var series = activity.getSeries()[parseInt(index[1])];
    	
		this.mc.clearMap();
    	if( series.findNearestValidLocationSample(0,1) != null ) {
			this.mc.drawTrack(series);
    	}
    },
    
	/**Sets the size of the select options to zero which essentially clears it from 
	 * any values.
	 * @private
	 */
    _clearHtmlSelect: function(select) {
		if(select) {
			//select.size = 0;
			select.options.size = 0;
		}
    },
    
	
	/** Finds last known point
	 */
   	_findLastKnownPoint: function(activities) 
   	{	
   	    var latestSeriesEndDate = null;
   	    var latestPosition = null;
   	    
		// loop through each activity
		for (var i = 0; i < activities.length; i++) 
		{
			var activity = activities[i];
			var series = activity.getSeries();
			
			// loop through each series in the activity
			for (var j = 0; j < series.length; j++) 
			{
				var curSeries = series[j];		
				
				switch(curSeries.getSeriesType()) 
				{
					case Garmin.Series.TYPES.history:
					    var endDate = activity.getSummaryValue(Garmin.Activity.SUMMARY_KEYS.endTime).getValue();
					    
					    if ((latestSeriesEndDate == null) || (endDate > latestSeriesEndDate))
					    {
					        latestPosition = curSeries.getLastValidLocationSample();
					    }
						break;
				}	
			}
		}
		
		if (latestPosition != null)
		{
		    this.lastKnownLocationText.innerText = "(" + latestPosition.getLatitude() + ":" + latestPosition.getLongitude() +")";
		    this.lastKnownLocationText.onclick = function() {	
					this.mc.clearMap();
		        	this.mc.addMarker(latestPosition.getLatitude(), latestPosition.getLongitude());
		        	this.mc.centerAndScale(latestPosition.getLatitude(), latestPosition.getLongitude(), 15);
		        	
		       	}.bind(this)
		}
	},

    onException: function(json) {
	    this.handleException(json.msg);
    },
    

	handleException: function(error) {
		var msg = error.name + ": " + error.message;	
		if (Garmin.PluginUtils.isDeviceErrorXml(error)) {
			msg = Garmin.PluginUtils.getDeviceErrorMessage(error);	
		}
	    this.setStatus(msg);
	    alert(msg);
	},

	setStatus: function(statusText) {
	    this.status.innerHTML = statusText;
	},
};