/* ----------------------------------------------------------------------------
 * Name: positionObject (obect)
 * Creates a Vars object.
 * Parameters:
 *   t: Time this position is valid for.
 *   p: google.maps.LatLng of new position.
 *   v: Variable data for satellite information.
 * Returns: Pointer to itself
 */
function satPositionObject(t, p, v) {
  this.time = t;
  this.point = p;
  this.vars = v;

  return this;
} // satPositionObject

/* ----------------------------------------------------------------------------
 * Name: Satellite (obect)
 * Creates a Vars object.
 * Parameters:
 *  nm: Satellite index number.
 *   n: Timestamp from server of satellites current position.
 *   m: Gmarker for satellite.
 *   nam: Satellite name for label.
 *   i: Image name of the marker icon.
 * Returns: Pointer to itself
 */
function Satellite(nm, n, m, nam, i) {
  this.timeIndex = n;
  this.number = nm;
  this.name = nam;
  this.positionCount = 0;
  this.marker = m;
  this.label = undefined;
  this.trackingLabel = undefined;
  this.polylines = new Array();
  this.positions = new Map();
  this.circle = undefined;
  this.compassLine = undefined;
  this.isCompassLine = false;
  this.isGroundCircle = false;
  this.isTracking = false;
  this.trailPoints = new Array();
  this.trailPolylines = new Array();
  this.trailSkipCount = 0;
  this.trailPolylineCount = 0;
  this.image = i;

  return this;
} // Satellite

Satellite.prototype.enableCompassLine = function() {
  this.isCompassLine = true;
} // Satellite::enableCompassLine

Satellite.prototype.disableCompassLine = function() {
  if (this.compassLine != undefined) {
    this.compassLine.setMap(null);
    this.compassLine = undefined;
  } // if

  this.isCompassLine = false;
} // Satellite::enableCompassLine

Satellite.prototype.enableGroundCircle = function() {
  this.isGroundCircle = true;
} // Satellite::enableGroundcircle

Satellite.prototype.disableGroundCircle = function() {
  if (this.circle != undefined) {
    this.circle.setMap(null);
    this.circle = undefined;
  } // if

  this.isGroundCircle = false;
} // Satellite::disableGroundCircle

Satellite.prototype.enableTracking = function() {
  this.enableGroundCircle();
  this.enableCompassLine();
  this.isTracking = true;

  return;
} // Satellite::enableTracking

Satellite.prototype.disableTracking = function() {
  this.disableGroundCircle();
  this.disableCompassLine();
  this.isTracking = false;

  return;
} // Satellite::disableTracking

/* ----------------------------------------------------------------------------
 * Name: syncIndex
 * Updates the current reference time index for satellite.
 * Parameters:
 *   t: Timestamp of new position.
 *   i: Image path of the image to set to.
 * Returns: Updated time index.
 */
Satellite.prototype.syncIndex = function(t, i) {
  this.timeIndex = t;

  if (i != this.image) {
    var img = "http://www.opensats.net/images/icons/satellite/small/" + i;

    var icon = new google.maps.MarkerImage(
      img,                            // url
      new google.maps.Size(22, 22),   // size
      new google.maps.Point(0, 0),    // origin
      new google.maps.Point(11, 11),    // anchor
      new google.maps.Size(22, 22)    // scaledSize
    );

    this.marker.setIcon(img);
    this.image = i;
    log('Satellite: Image change for satellite number '+this.number+'.');
  } // if

  return t;
} // Satellite::syncIndex

/* ----------------------------------------------------------------------------
 * Name: addPosition
 * Adds a new future position to the map.
 * Parameters:
 *   t: Timestamp of new position.
 *   p: google.maps.LatLng position point.
 *   v: Variable satellite data.
 * Returns: True if add was succesful, false if key already existed.
 */
Satellite.prototype.addPosition = function(t, p, v) {
  if (this.positions.isKey(t))
    return false;

  var np = new satPositionObject(t, p, v);

  this.positions.add(t, np);

  return true;
} // Satellite::addPosition

/* ----------------------------------------------------------------------------
 * Name: removePastPositions
 * Adds a new future position to the map.
 * Parameters:
 *   t: Timestamp of new position.
 * Returns: True if add was succesful, false if key already existed.
 */
Satellite.prototype.removePastPositions = function(t) {
  var keys = this.positions.getKeys();
  var num_rows = 0;

  var keys = this.positions.getKeys();
  for(var i=0; i < keys.length; i++) {
    if (keys[i] < this.timeIndex) {
      this.positions.remove(keys[i]);
      num_rows++;
    } // if
  } // for

  return num_rows;
} // Satellite::removePastPositions

/* ----------------------------------------------------------------------------
 * Name: incrementPosition
 * Moves satellite to next position.
 * Parameters:
 *   none
 * Returns: True if add was succesful, false if key already existed.
 */
Satellite.prototype.incrementPosition = function() {
  this.timeIndex++;
  this.positionCount++;
  this.removePastPositions();

  if (!this.positions.isKey(this.timeIndex)) {
    // Show when clock is out of sync.
    changeById('clock', this.timeIndex);
    return false;
  } // if

  var p = this.positions.get(this.timeIndex);

  this.changePosition(p);
  this.plotTrail(p);

  var lat = p.point.lat();
  var lng = p.point.lng();

  // Try and draw ground circle if possible.
  if (p.vars.isName("rng")) {
    var rng = parseFloat(p.vars.getField("rng"));
    this.plotGroundCircle(lat, lng, (rng*1000/2));
  } // if

  return true;
} // Satellite::incrementPosition

/* ----------------------------------------------------------------------------
 * Name: changePosition
 * Changes a satellites actual position on Google Maps.
 * Parameters:
 *   p: google.maps.LatLng point.
 * Returns: True if add was succesful, false if key already existed.
 */
Satellite.prototype.changePosition = function(p) {
//  if (this.label != undefined)
//    this.label.setMap(null);

  // Create new label for satellite.
//  var label = new ELabel(p.point, this.name, "satLabelName", new GSize(-50, +20), 90);

  this.marker.set('position', p.point);
//  this.label = label;

//  label.setMap(map);

  // Recenter map to current satellite position if we're
  // tracking it.
  if (isChecked('OpenSATS:Form:Autocenter')
      && (this.timeIndex % 2) == 0
      && this.isTracking)
    map.setCenter(p.point);

  return true;
} // Satellite::changePosition

/* ----------------------------------------------------------------------------
 * Name: changeTrackingLabel
 * Changes a satellites actual position on Google Maps.
 * Parameters:
 *   p: Point to change label for.
 * Returns: True if add was succesful, false if key already existed.
 */
Satellite.prototype.changeTrackingLabel = function(p) {
  if (!this.positions.isKey(this.timeIndex))
    return false;

//  if (this.trackingLabel != undefined)
//    this.trackingLabel.setMap(null);

  if (!this.isTracking) {
    this.marker.set('labelVisible', false);
    this.trackingLabel = undefined;
    return false;
  } // if

  var p2 = this.positions.get(this.timeIndex);

  v = p2.vars;
  if (!v.isName("alt"))
    return false;

  var lat1 = p.lat();
  var lng1 = p.lng();
  var lat2 = this.marker.getPosition().lat();
  var lng2 = this.marker.getPosition().lng();
  var alt = parseFloat(v.getField("alt"));

  var bearing = Math.round(LatLon.bearing(lat1, lng1, lat2, lng2)*10)/10;

  var out = "Az: " + bearing + "&deg;"

  var elevation = Math.round( toElevation2(lat1, lng1, 0, lat2, lng2)*10)/10;
  var el = round(toElevation3(lat1, lng1, 0, lat2, lng2, alt), 1);
  if (this.number == "-1" || this.number == "-2")
    el = round(Elevation2(lng2, lat1, lng1, 0), 1);

  out += " El: " + el + "&deg;";

  out += "<br />Alt: " + round(alt, 1) + "mi";

//  var label = new ELabel(new google.maps.LatLng(lat2, lng2), out,"satLabel",new GSize(-20,90), 60);

//  this.trackingLabel = label;

//  label.setMap(map);

  this.marker.set('labelContent', out);
  this.marker.set('labelVisible', true);

  var alt = parseFloat(v.getField("alt"));
  var el = round(toElevation(lat1,lng1,lng2), 2);
  var el2 = round(toElevation3(lat1,lng1, 0,lat2, lng2, alt), 2);
  if (this.number == "-1" || this.number == "-2")
    el2 = round(toElevation(lat1, lng1, lng2), 2);
  var d = round(toDistance(lat1, lng1, lat2, lng2), 2);
  var los = alt.toGroundRange();
  
  var dte = new Date();
  var day = dte.getUTCDate();
  var mon = dte.getUTCMonth()+1;
  var year = dte.getUTCFullYear();
  var hour = dte.getUTCHours();
  var min = dte.getUTCMinutes();
  var second = dte.getUTCSeconds();
  var day =  day + ((hour/24+(min/1440)+(second/86400)));
  //var daynum = JulianDayNumber(day, mon, year, hour, min, second);
  var daynum2 = calcJulianDayNumber(day, mon, year);
  //var daynum = dte.julianDate();
  //var today = new Date();
  //var daynum = Math.floor((today.valueOf() / (1000 * 60 * 60 * 24)) - 0.5) + 2440588;
  var daynum = Cal2JD(year, mon, dte.getUTCDate(), hour, min, second);

  // Change side info stuff
  var info = "<b>" + this.name + "</b><br />"
             + '<hr style="color: grey;" size="1" noshade />'
             + " Lat: " + lat2 + "<br />"
             + " Lng: " + lng2 + "<br />"
             + "  Az: " + Math.round(LatLon.bearing(lat1, lng1, lat2, lng2)*100)/100 + "&deg;<br />"
             + "  El: " + el2 + "&deg;<br />"
             + " Alt: " + Math.round(parseFloat(v.getField("alt"))*10)/10 + "mi<br />"
             + "Dist: " + d + "mi<br />"
             + " LOS: " + round(los, 2) + "mi<br />"
             + "DayNum: " + round(daynum, 5) + "<br />"
//             + "DayNum: " + round(daynum2, 5) + "<br />"
             + "Now: " + mon + "-" + dte.getUTCDate() + "-" + year + " " + hour + ":" + min + ":" + second + "<br />"
             + '<hr style="color: grey;" size="1" noshade />';

  changeById('tmpInfo', info);

  return true;
} // Satellite::changeTrackingLabel

/* ----------------------------------------------------------------------------
 * Name: plotTrail
 * Plots a trail line behind satellite as it moves.
 * Parameters:
 *   p: google.maps.LatLng point.
 * Returns: n/a
 */
Satellite.prototype.plotTrail = function(p) {
  if (!this.trailSkipCount) {
    this.trailPoints.push(p.point);

    if (this.trailPoints.length == 8) {
      var trailPolyline = new google.maps.Polyline({
        path: this.trailPoints,
        strokeColor: (this.trailPolylineCount % 2 == 0) ? 'white' : 'grey',
        strokeWeight: 2,
        storkeOpacity: 0.8
      });
      trailPolyline.setMap(map);
      this.trailPolylines.push(trailPolyline);
      this.trailPoints = new Array();
      this.trailSkipCount++;
      this.trailPolylineCount++;
    } // if
  } // if
  else {
    this.trailSkipCount++;
    if (this.trailSkipCount > 4)
      this.trailSkipCount = 0;
  } // else

  // Loop through list of polylines and remove excess.
  if (this.trailPolylines.length == 6) {
    var tmpTrailPolylines = new Array();
    for(var i=0; i < this.trailPolylines.length; i++) {
      if (i == 0)
        this.trailPolylines[i].setMap(null);
      else
        tmpTrailPolylines.push(this.trailPolylines[i]);
    } // for

    this.trailPolylines = tmpTrailPolylines;
  } // if

  return;
} // Satellite::plotTrail

/* ----------------------------------------------------------------------------
 * Name: remove
 * Remove all markers associated with this satellite.
 * Parameters:
 *   none
 * Returns: True if remove was succesful, false if key didn't exist.
 */
Satellite.prototype.remove = function() {
  // Remove tracking settings and objects.
  this.disableTracking();

  // Remove GMarker itself.
  this.marker.setMap(null);

  // Remove Elabel for marker.
  //this.label.setMap(null);

  // Loop through path google.maps.Polyline points and remove them.
  for(var i in this.polylines)
    this.polylines[i].setMap(null);

  this.polylines = [];
  this.positions.clear();

  for(var i=0; i < this.trailPolylines.length; i++)
    this.trailPolylines[i].setMap(null);

  if (this.trackingLabel != undefined)
    this.trackingLabel.setMap(null);

  return true;
} // Satellite::remove

/* ----------------------------------------------------------------------------
 * Name: plotGroundCircle
 * Plots a circle given a lat/lng pair and range.
 * Parameters:
 *    la: Latitude of circle center.
 *    lo: Longitude of circle center.
 *   rng: Range of the circle.
 * Returns: Returns the number of points created.
 */
Satellite.prototype.plotGroundCircle = function(la, lo, rng) {
  var num_points = 0;
  var points = new Array();
  var lat = la.toRad();
  var lng = lo.toRad();

  if (this.circle != undefined) {
    this.circle.setMap(null);
    this.circle = undefined;
  } // if

  if (!this.isGroundCircle)
    return 0;

  var d_rad = rng/6378137;

  // loop through the array and write path linestrings
  for(var i=0.0; i<=360.0; i += 3.75) {
    var radial = i.toRad();

    var lat_rad = Math.asin(Math.sin(lat)*Math.cos(d_rad) + Math.cos(lat)*Math.sin(d_rad)*Math.cos(radial));
    var dlon_rad = Math.atan2(Math.sin(radial)*Math.sin(d_rad)*Math.cos(lat), Math.cos(d_rad)-Math.sin(lat)*Math.sin(lat_rad));

    var lon_rad = ((lng+dlon_rad + Math.PI) % (2*Math.PI)) - Math.PI;
    var point = new google.maps.LatLng(lat_rad.toDeg(), lon_rad.toDeg());
    points.push(point);
    num_points++;
  } // for

  if (points.length > 0) {
    this.circle = new google.maps.Polygon({
      path: points,
      strokeColor: 'white',
      strokeWeight: 1,
      strokeOpacity: 0.5,
      fillColor: 'white',
      fillOpacity: 0.08
    });

    this.circle.setMap(map);
  } // if

  return num_points;
} // Satellite::plotGroundCircle

/* ----------------------------------------------------------------------------
 * Name: plotCompassLine
 * Plots a compass line from the Satellite to the given google.maps.LatLng.
 * Parameters:
 *    p: google.maps.LatLng to draw line to.
 * Returns: n/a
 */
Satellite.prototype.plotCompassLine = function(p) {
  if (this.compassLine != undefined)
    this.compassLine.setMap(null);

  if (!this.isCompassLine)
    return false;

  var points = new Array(this.marker.getPosition(), p);

  this.compassLine = new google.maps.Polyline({
    path: points,
    strokeColor: 'white',
    strokeWeight: 2,
    strokeOpacity: 0.8
  });

  this.compassLine.setMap(map);

  return true;
} // Satellite::plotCompassLine

/* ----------------------------------------------------------------------------
 * Name: removeCompassLine
 * Plots a compass line from the Satellite to the given google.maps.LatLng.
 * Parameters:
 *   none
 * Returns: n/a
 */
Satellite.prototype.removeCompassLine = function() {
  if (this.compassLine == undefined)
    // Non compass line was plotted.
    return false;

  this.compassLine.setMap(null);

  return true;
} // Satellite::removeCompassLine

