/*************************************************************************
 *
 * SatelliteCalculator v1.0
 *
 * C. James Callaway, September 17, 2007
 * Copyright 2007 C&C Techchnologies, Inc.
 *
 * satelliteData.js contains the current dataset
 *
 * Tested against Firefox 2.0.0.6, IE6, IE7, and Safari 3.0.3
 *
 *------------------------------------------------------------------------
 *
 * Dataset is an array of SatelliteGroups.
 *
 * SatelliteGroupObj {
 *	headers: array(string),	// Group headers for table
 *	satinfo: array(SatelliteInfoObj)
 * }
 *
 * SatelliteInfoObj {
 * 	name: string,		// Name of the Satellite
 * 	description: string,	// Descriptive entry for Satellite
 *	satLong: float,		// Longitude of the Satellite
 *	prefix: string,		// A unique prefix for elements
 * }
 *
 *------------------------------------------------------------------------
 *
 *
 * satelliteData = [
 *	{
 *	  headers: [ "1","2","3","4","5","6" ],
 * 	  satinfo: [
 *			{
 *		   	     name: "INMARSAT 3-F5",
 *		      description: "NET1",
 *			  satLong: 25,
 *			   prefix: 'I3F5'
 *			},
 *			{
 *			     name: "INMARSAT 2-F4",
 *		      description: "NET2",
 *			  satLong: 109,
 *			   prefix: 'I2F4'
 *			}
 *                 ];
 *       }
 *  ];
 *
 * <html>
 *   <form>
 *     <input type='text' id='latitude'/>
 *     <input type='text' id='longitude'/>
 *     <input type='button'
 *	  onclick='calculator.calculate(this.form.serialize(true));'/>
 *   </form>
 *   <table>
 *	<tbody id='tableContainer'/>
 *   </table>
 *   <span id='latitudeDisplay'></span>
 *   <span id='longitudeDisplay'></span>
 * </html>
 *
 * calculator = new SatelliteCalculator('tableContainer',satelliteData);
 * calculator.calculate({latitude:30,longitude:-92});
 *
 *
 */
SatelliteCalculator = Class.create();
SatelliteCalculator.prototype = {
    initialize:function(tableContainer,satelliteData){
	this.data = satelliteData;
	this.table = $(tableContainer);
	this.suffixes = ['Azimuth','Elevation','Range'];
	this.options = {
	    elevationColorMap:elevationColorMap,
	    updateDisplay: true
	};
	this.data.each(this._buildSatelliteGroup.bind(this));
    },
    calculate:function(inputData){
	if (!this._validate(inputData.latitude,inputData.longitude))
	    return;

	var latitude = parseFloat(inputData.latitude);
	var longitude = parseFloat(inputData.longitude);

	if (this.options.updateDisplay)
	    this._updateDisplay (latitude,longitude);
	this._calculate(latitude,longitude);
    },
    _buildSatelliteGroup:function(satelliteGroupData){
	this.table.appendChild(
	    this._buildRow('TH',satelliteGroupData.headers)
	    );
	satelliteGroupData.satinfo.each(this._buildSatelliteRow.bind(this));
    },
    _buildRow:function(elementType,cells){
	var row = document.createElement('TR');
	cells.each(function(cell){
		       var cellElement = document.createElement(elementType);
		       cellElement.appendChild(document.createTextNode(cell));
		       row.appendChild(cellElement);
		   });
	return row;
    },
    _buildSatelliteRow:function(sat){
	var row = document.createElement('TR');
	row.id = sat.prefix + "Row";

	var str = Math.abs(sat.satLong).toString()  
	    + entity("&#176;") + " " + ((sat.satLong < 0) ? "W" : "E");

	[ {name:sat.name,textAlign:'left',nodeType:'TD'},
	  {name:str,textAlign:'center',nodeType:'TD'},
	  {name:sat.description,textAlign:'center',nodeType:'TD'}
	].each(
	    function(r){
		var textNode = document.createTextNode(r.name);
		var cell = document.createElement(r.nodeType);
		cell.style.textAlign = r.textAlign;
		cell.appendChild(textNode);
		row.appendChild(cell);
	    });

	this.suffixes.each(
	    function(suffix){
		var textNode = document.createTextNode(' ');
		var cell = document.createElement('TD');
		cell.id = sat.prefix + suffix;
		cell.style.textAlign = 'center';
		cell.appendChild(textNode);
		row.appendChild(cell);
	    });
	this.table.appendChild(row);
    },
    _updateDisplay:function(latitude,longitude){
	$('latitudeDisplay').update(latitude);
	$('longitudeDisplay').update(longitude);
    },
    _calculate:function(latitude,longitude){
	this.latitude = latitude;
	this.longitude = longitude;
	var _this = this;
	this.data.each(
	    function(group){group.satinfo.each(_this._updateRow.bind(_this))})
    },
    _updateRow:function(sat){
	var elms=[
	    sat.prefix+"Azimuth",
	    sat.prefix+"Elevation",
	    sat.prefix+"Range"
	    ];
	var rowName = sat.prefix+"Row";

	elev = elevation(sat.satLong,this.latitude,this.longitude);
	this._updateRowColor(rowName,elev);

	$(elms[0]).update(azimuth(sat.satLong,this.latitude,this.longitude) + entity("&#176;"));
	if (elev<=0) elev = "<0";
	$(elms[1]).update(elev + entity("&#176;"));
	$(elms[2]).update(satelliteRange(sat.satLong,this.latitude,this.longitude));
    },
    _updateRowColor:function(element,elevation){
	element=$(element);
	if (this.options.elevationColorMap){
	    var colors = this.options.elevationColorMap(elevation);
	    element.setStyle({ backgroundColor: colors.backgroundColor,
			       color: colors.color});
	}
    },
    _validate:function(latitude,longitude){
	latitude = parseFloat(latitude);
	longitude = parseFloat(longitude);

	if (isNaN(latitude)
	    || isNaN(longitude)
	    || latitude==null
	    || longitude==null
	    || latitude<-90
	    || latitude>90
	    || longitude<-180
	    || longitude>180
	    || latitude.length==0
	    || longitude.length==0){
	    alert("\nPlease Enter your C-Nav GPS Receiver Position in degrees of Latitude and Longitude (decimal)");
	    return false;
	}
	return true;
    }
};

function azimuth(satLongitude,latitude,longitude) {
	if (satLongitude==longitude && latitude==0) return 0;
	lon_delta=(longitude-satLongitude);
	if (lon_delta<-180) lon_delta=360+lon_delta;
	if (lon_delta>180) lon_delta=360-lon_delta;
	radian_lon=lon_delta/57.29578;
	comp_azumith=180+(57.29578*Math.atan(Math.tan(radian_lon)/Math.sin(latitude/57.29578)));
	if (latitude<0) comp_azumith=comp_azumith-180.0;
	if (comp_azumith<0) comp_azumith=comp_azumith+360.0;
	if (lon_delta<-90) comp_azumith=comp_azumith-180.0;
	if (lon_delta>90) comp_azumith=comp_azumith+180.0;
	comp_azumith=(Math.round(comp_azumith));
	return comp_azumith;
}
function elevation(satLongitude,latitude,longitude) {
	radian_lon=(longitude-satLongitude)/57.29578;
	radian_lat=latitude/57.29578;
	d=1+35786/6378.16;
	a=d*Math.cos(radian_lat)*Math.cos(radian_lon)-1;
	b=d*Math.sqrt(1-Math.cos(radian_lat)*Math.cos(radian_lat)*Math.cos(radian_lon)*Math.cos(radian_lon));
	interim_elev=57.29578*Math.atan(a/b);
	if (interim_elev<30)
		comp_elevation=(interim_elev+Math.sqrt(interim_elev*interim_elev+4.132))/2
			else
				comp_elevation=interim_elev;
	comp_elevation=(Math.round(comp_elevation));
//	if (comp_elevation<5) comp_elevation="< 5";
	return comp_elevation;
}
function satelliteRange(satLongitude,latitude,longitude) {
	radian_lon=(longitude-satLongitude)/57.29578;
	h=35786*35786;
	o=2*6378.16*(35786+6378.16);
	a=1-Math.cos(latitude/57.29578)*Math.cos(-radian_lon);
	comp_distance=Math.sqrt(h+o*a);
	comp_distance=Math.round(comp_distance);
	return comp_distance;
}
function entity(str,stupefy){
    var
        e=document.createElement("div");
    e.innerHTML=String(str);

    if (stupefy)
        return '&#'+e.innerHTML.charCodeAt(0)+';';
    return e.innerHTML;
}
