// =========================================================
// Object for handling the display of gliders in a div layer
// =========================================================

// Glider info object
function GliderObj()
{
  this.PlayerID = "";
  this.Nick = "";
  this.Name = "";
  this.Aircraft = "";
  this.Reg = "";
  this.CompID = "";
  this.TaskDistance = 0;
  this.TaskTime = 0;
  this.TaskAvg = 0;
  this.Latitude = 0;
  this.Longitude = 0;
  this.Altitude = 0;
  this.Speed = 0;
  this.Heading = 360;
  this.Status = "";
  this.ImgDivElem = null;
  this.CycleInfoDiv = null;
  this.InfoDivElem = null;
}

// ============================================================================
// Glider div layer object
// ============================================================================
function GliderLayerObj(DivID, ServerID)
{
  this.ParentDiv = document.getElementById(DivID); // Store div element that we use as our "canvas"
  this.PositionReq = null;                   // Glider XMLHttpRequest position request object
  this.RequestInProgress = false;
  this.ServerID = ServerID;                  // The ID of the MP server
  this.Gliders = new Array();                // Glider info array
  this.ExtentNorth = 0.0;
  this.ExtentSouth = 0.0;
  this.ExtentWest = 0.0;
  this.ExtentEast = 0.0;
  this.PixelSizeLat = 0.0;
  this.PixelSizeLon = 0.0;

  this.InfoCycleCounter = 0;
  this.CycleTimerID = null;
  this.Initialized = false;
}

// ============================================================================
// Request new glider data
// ============================================================================
GliderLayerObj.prototype.RequestPositions = function()
{
  // Don't process a new request until the old one is finished
  if (!this.RequestInProgress)
  {
    this.RequestInProgress = true;

    // branch for native XMLHttpRequest object
    if (window.XMLHttpRequest)
    {
      this.PositionReq = new XMLHttpRequest();
      this.PositionReq.onreadystatechange = this.ProcessPositionReqChange.bindEventListener(this);
      this.PositionReq.open("GET", "get_positions.php?ServerID="+this.ServerID, true);
      this.PositionReq.send(null);
    }
    else  // branch for IE/Windows ActiveX version
    {
      if (window.ActiveXObject)
      {
        this.PositionReq = new ActiveXObject("Microsoft.XMLHTTP");
        if (this.PositionReq)
        {
          this.PositionReq.onreadystatechange = this.ProcessPositionReqChange.bindEventListener(this);
          this.PositionReq.open("GET", "get_positions.php?ServerID="+this.ServerID, true);
          this.PositionReq.send(null);
        }
      }
    }
  }
}

// ============================================================================
GliderLayerObj.prototype.ProcessPositionReqChange = function()
{
  // readyState 4 = complete
  if (this.PositionReq.readyState == 4)
  {
    // HTTP result status 200 = OK
    if (this.PositionReq.status == 200)
    {
      var Response = this.PositionReq.responseXML.documentElement;
      var GliderElements = Response.getElementsByTagName('glider_pos');

      var GliderElem = null;
      var GliderInfo = null;
      var PlayerIDArr = new Array();

      // Update/store glider info
      for (var i = 0; i < GliderElements.length; i++)
      {
        GliderElem = GliderElements.item(i); // Put a single glider position element from the XML into GliderPos
        GliderInfo = new GliderObj();
        GliderInfo.PlayerID = GliderElem.getElementsByTagName('player_id').item(0).firstChild.data;
        GliderInfo.Nick = GliderElem.getElementsByTagName('nick').item(0).firstChild.data;
        GliderInfo.Name = GliderElem.getElementsByTagName('name').item(0).firstChild.data;
        GliderInfo.Aircraft = GliderElem.getElementsByTagName('aircraft').item(0).firstChild.data;
        GliderInfo.Reg = GliderElem.getElementsByTagName('reg').item(0).firstChild.data;
        GliderInfo.CompID = GliderElem.getElementsByTagName('comp_id').item(0).firstChild.data;
        GliderInfo.TaskDistance = GliderElem.getElementsByTagName('task_dist').item(0).firstChild.data;
        GliderInfo.TaskTime = GliderElem.getElementsByTagName('task_time').item(0).firstChild.data;
        if(GliderInfo.TaskTime > 0)
           GliderInfo.TaskAvg = GliderInfo.TaskDistance / (GliderInfo.TaskTime / 3600);
        else
           GliderInfo.TaskAvg = 0.0;
	
//        GliderInfo.TaskAvg = GliderInfo.TaskDistance / (GliderInfo.TaskTime / 3600);
        GliderInfo.Latitude = GliderElem.getElementsByTagName('lat').item(0).firstChild.data;
        GliderInfo.Longitude = GliderElem.getElementsByTagName('lon').item(0).firstChild.data;
        GliderInfo.Altitude = GliderElem.getElementsByTagName('alt').item(0).firstChild.data;
        GliderInfo.Speed = GliderElem.getElementsByTagName('spd').item(0).firstChild.data;
        GliderInfo.Heading = GliderElem.getElementsByTagName('hdg').item(0).firstChild.data;
        GliderInfo.Status = GliderElem.getElementsByTagName('status').item(0).firstChild.data;

        // Add the Player IDs to an array to make it easier for use to do the glider array syncing
        PlayerIDArr.push(GliderInfo.PlayerID);

        this.UpdateGlider(GliderInfo);
        delete GliderInfo;
      }

      // Remove gliders that are not in the new list but are still in the Gliders array from a previous
      // data update. i.e. Keep the gliders in sync with the DB and remove old ones as they quit flying.
      var Found;
      for (var i = 0; i < this.Gliders.length; i++)
      {
        GliderInfo = this.Gliders[i];
        Found = false;

        for (var j = 0; j < PlayerIDArr.length; j++)
        {
          if (GliderInfo.PlayerID == PlayerIDArr[j])
          {
            Found = true;
          }
        }

        if (!Found)
        {
          this.DeleteGlider(GliderInfo.PlayerID);
        }
      }

      delete PlayerIDArr;

      // Allow updating again
      this.RequestInProgress = false;

       // Get the info label cycling running after the first batch of data has been retrieved
      if (!this.Initialized)
      {
        this.Initialized = true;
        this.CycleGliderInfo();
        this.GliderInfoCycleTimerOnOff(true);
      }

      // Update glider list in menu bar
//      parent.menubarframe.window.frames.gliderframe.UpdateList(this.Gliders);
    }
    else
    {
      alert("There was a problem retrieving the glider position XML data:\n" + this.PositionReq.statusText);
    }
  }
}

// ============================================================================
GliderLayerObj.prototype.RefreshData = function ()
{
  // Get new positions
  this.RequestPositions();
}

// ============================================================================
GliderLayerObj.prototype.UpdateExtents = function (ExtentEast, ExtentWest, ExtentNorth, ExtentSouth)
{
  // Store the extents and calculate the geographic pixel size
  this.ExtentNorth = ExtentNorth;
  this.ExtentSouth = ExtentSouth;
  this.ExtentWest = ExtentWest;
  this.ExtentEast = ExtentEast;
/*
  this.PixelSizeLat = (ExtentNorth - ExtentSouth) / 800;
  this.PixelSizeLon = (ExtentEast - ExtentWest) / 600;
*/

  this.PixelSizeLat = (ExtentNorth - ExtentSouth) / parseInt(this.ParentDiv.style.height);
  this.PixelSizeLon = (ExtentEast - ExtentWest) / parseInt(this.ParentDiv.style.width);

  this.ReCalcGliderPositions();
}

// ============================================================================
GliderLayerObj.prototype.AddGlider = function (GliderInfo)
{
  var Span;
  var Glider = new GliderObj(); // Create a new glider object
  Glider = GliderInfo;          // Copy the object contents across
  this.Gliders.push(Glider);    // Store new glider object into array

  // div element containing the glider image
  Glider.ImgDivElem = document.createElement("div");
  Glider.ImgDivElem.className = "glider_img_elem";
  Glider.ImgDivElem.onmouseover = Glider.MouseOver.bindEventListener(Glider);
  Glider.ImgDivElem.onmouseout = Glider.MouseOut.bindEventListener(Glider);
  Glider.ImgDivElem.style.zIndex = 1;
  this.CalcAndSetGliderImg(Glider);
  this.ParentDiv.appendChild(Glider.ImgDivElem);

  // div element containing a cycling display
  Glider.CycleInfoDiv = document.createElement("div");
  Glider.CycleInfoDiv.className = "glider_cycle_info_elem";
  Glider.CycleInfoDiv.style.zIndex = 0;
  this.ParentDiv.appendChild(Glider.CycleInfoDiv);

  // div element containing the glider info
  Glider.InfoDivElem = document.createElement("div");
  Glider.InfoDivElem.className = "glider_info_elem";
  // Setting the width and height in the CSS style sheet works for display purposes
  // but then for some odd reason you can't read it with JS
  Glider.InfoDivElem.style.width = 200;
  Glider.InfoDivElem.style.height = 145;
  Glider.InfoDivElem.style.zIndex = 2;
  this.ParentDiv.appendChild(Glider.InfoDivElem);

  // A whole stack of span elements placed inside Glider.InfoDivElem
  // Pilot nick
  Span = document.createElement("span");
  Span.className = "pilot_nick";
  Span.id = "pilot_nick";
  Span.innerHTML = Glider.Nick;
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Pilot name
  Span = document.createElement("span");
  Span.className = "pilot_name";
  Span.id = "pilot_name";
  Span.innerHTML = "("+Glider.Name+")";
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Glider type
  Span = document.createElement("span");
  Span.className = "aircraft";
  Span.id = "aircraft";
  Span.innerHTML = Glider.Aircraft + "\u00a0\u00a0\u00a0\u00a0";
  Glider.InfoDivElem.appendChild(Span);

  // Competition number
  Span = document.createElement("span");
  Span.className = "comp_number";
  Span.id = "comp_number";
  Span.innerHTML = Glider.CompID + "\u00a0\u00a0\u00a0\u00a0";
  Glider.InfoDivElem.appendChild(Span);

  // Registration number
  Span = document.createElement("span");
  Span.className = "reg_number";
  Span.id = "reg_number";
  Span.innerHTML = Glider.Reg;
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Speed
  Span = document.createElement("span");
  Span.className = "glider_speed";
  Span.id = "glider_speed";
  Span.innerHTML = (Math.round(3.6 * Glider.Speed * 100) / 100) + " km/h\u00a0\u00a0\u00a0\u00a0";
  Glider.InfoDivElem.appendChild(Span);

  // Heading
  Span = document.createElement("span");
  Span.className = "glider_heading";
  Span.id = "glider_heading";
  Span.innerHTML = Math.round(Glider.Heading) + "\u00b0\u00a0\u00a0\u00a0\u00a0";
  Glider.InfoDivElem.appendChild(Span);

  // Altitude
  Span = document.createElement("span");
  Span.className = "glider_altitude";
  Span.id = "glider_altitude";
  Span.innerHTML = Math.round(Glider.Altitude) + " MSL";
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Task distance heading
  Span = document.createElement("span");
  Span.className = "glider_info_task_headings";
  Span.innerHTML = "Task distance : ";
  Glider.InfoDivElem.appendChild(Span);
  // Distance
  Span = document.createElement("span");
  Span.className = "glider_distance";
  Span.id = "glider_distance";
  Span.innerHTML = (Math.round(Glider.TaskDistance * 100) / 100) + " km";
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Average speed heading
  Span = document.createElement("span");
  Span.className = "glider_info_task_headings";
  Span.innerHTML = "Average speed : ";
  Glider.InfoDivElem.appendChild(Span);
  // Avg speed
  Span = document.createElement("span");
  Span.className = "glider_avg_speed";
  Span.id = "glider_avg_speed";
  Span.innerHTML = (Math.round(Glider.TaskAvg * 100) / 100) + " km/h";;
  Glider.InfoDivElem.appendChild(Span);
  Glider.InfoDivElem.appendChild(document.createElement("br"));

  // Status heading
  Span = document.createElement("span");
  Span.className = "glider_info_task_headings";
  Span.innerHTML = "Status : ";
  Glider.InfoDivElem.appendChild(Span);
  // Status
  Span = document.createElement("span");
  Span.className = "glider_status";
  Span.id = "glider_status";
  Span.innerHTML = Glider.Status;
  Glider.InfoDivElem.appendChild(Span);

  this.CalcAndSetGliderPos(Glider);
}

// ============================================================================
// If a glider does not exist it will be automatically added to this.Gliders
// ============================================================================
GliderLayerObj.prototype.UpdateGlider = function (GliderInfo)
{
  // Store glider info into array if it doesn't exist in the array yet
  if (this.IndexOfGliderID(GliderInfo.PlayerID) == -1)
  {
    this.AddGlider(GliderInfo);
  }
  // else just update the glider info and position
  else
  {
    var Glider = this.Gliders[this.IndexOfGliderID(GliderInfo.PlayerID)];

    // Copy all the variable across - we can't just assign Glider to GliderInfo
    // otherwise we'll lose the references to all our div elements
    Glider.Nick         = GliderInfo.Nick;
    Glider.Name         = GliderInfo.Name;
    Glider.Aircraft     = GliderInfo.Aircraft;
    Glider.Reg          = GliderInfo.Reg;
    Glider.CompID       = GliderInfo.CompID;
    Glider.TaskDistance = GliderInfo.TaskDistance;
    Glider.TaskTime     = GliderInfo.TaskTime;
    Glider.TaskAvg      = GliderInfo.TaskAvg;
    Glider.Latitude     = GliderInfo.Latitude;
    Glider.Longitude    = GliderInfo.Longitude;
    Glider.Speed        = GliderInfo.Speed;
    Glider.Heading      = GliderInfo.Heading;
    Glider.Status       = GliderInfo.Status;

    // Update info div, span elements
    var ElemArr = Glider.InfoDivElem.getElementsByTagName("*");
    for (var i = 0; i < ElemArr.length; i++)
    {
      switch(ElemArr[i].id)
      {
        case "pilot_nick": ElemArr[i].innerHTML = Glider.Nick;
          break;
        case "pilot_name": ElemArr[i].innerHTML = "("+Glider.Name+")";
          break;
        case "aircraft": ElemArr[i].innerHTML = Glider.Aircraft + "\u00a0\u00a0\u00a0\u00a0";
          break;
        case "comp_number": ElemArr[i].innerHTML = Glider.CompID + "\u00a0\u00a0\u00a0\u00a0";
          break;
        case "reg_number": ElemArr[i].innerHTML = Glider.Reg;
          break;
        case "glider_speed": ElemArr[i].innerHTML = (Math.round(Glider.Speed * 100) / 100) + " km/h\u00a0\u00a0\u00a0\u00a0";
          break;
        case "glider_heading": ElemArr[i].innerHTML = Math.round(Glider.Heading) + "\u00b0\u00a0\u00a0\u00a0\u00a0";
          break;
        case "glider_altitude": ElemArr[i].innerHTML = Math.round(Glider.Altitude) + " MSL";
          break;
        case "glider_distance": ElemArr[i].innerHTML = (Math.round(Glider.TaskDistance * 100) / 100) + " km";
          break;
        case "glider_avg_speed": ElemArr[i].innerHTML = (Math.round(Glider.TaskAvg * 100) / 100) + " km/h";
          break;
        case "glider_status": ElemArr[i].innerHTML = Glider.Status;
      }
    }

    this.CalcAndSetGliderPos(Glider);
    this.CalcAndSetGliderImg(Glider);
  }
}

// ============================================================================
GliderLayerObj.prototype.DeleteGlider = function (PlayerID)
{
  var IndexToRemove = this.IndexOfGliderID(PlayerID);
  var NewGliderArray = new Array();
  var Glider = null;
  var DivElem = null;

  if (IndexToRemove != -1)
  {
    // Remove an element out of the array
    Glider = this.Gliders[IndexToRemove];
    NewGliderArray = this.Gliders.slice(0, IndexToRemove);
    NewGliderArray = NewGliderArray.concat(this.Gliders.slice(IndexToRemove + 1, this.Gliders.length));
    this.Gliders = NewGliderArray;

    // Delete the glider object and all it's elements
    // We do a manual clean up process since some browser's have a buggy automatic garbage
    // disposal system where it disposes of memory instead (mem leaks)
    DivElem = this.ParentDiv.removeChild(Glider.ImgDivElem);
    delete DivElem;
    DivElem = this.ParentDiv.removeChild(Glider.CycleInfoDiv);
    delete DivElem;
    DivElem = this.ParentDiv.removeChild(Glider.InfoDivElem);

    var SpanNodes = DivElem.childNodes;
    var SpanElem = null;
    for (var i = 0; i < SpanNodes.length; i++)
    {
      SpanElem = DivElem.removeChild(SpanNodes[i]);
      delete SpanElem;
    }

    delete DivElem;
    delete Glider;
  }
}

// ============================================================================
// This function recalculates the positions of all the glider div elements and
// does not request new data from the database.
// If you want to get new positions from the database call RefreshData instead.
// This function is called from UpdateExtents.
// ============================================================================
GliderLayerObj.prototype.ReCalcGliderPositions = function ()
{
  var Glider = null;
  for (var i = 0; i < this.Gliders.length; i++)
  {
    Glider = this.Gliders[i];
    this.CalcAndSetGliderPos(Glider);
  }
}

// ============================================================================
GliderLayerObj.prototype.IndexOfGliderID = function (PlayerID)
{
  var Found = false;
  var Count = 0;
  var Glider = null;

  while ((Count < this.Gliders.length) && !Found)
  {
    Glider = this.Gliders[Count];
    if (Glider.PlayerID == PlayerID)
    {
      Found = true;
    }
    else
    {
      Count++;
    }
  }

  if (Found)
  {
    return Count;
  }
  else
  {
    return -1;
  }
}

// ============================================================================
GliderLayerObj.prototype.CalcAndSetGliderPos = function (Glider)
{
  // Make sure we have some extents and pixel sizes to work with first
  if ((this.PixelSizeLat != 0.0) && (this.PixelSizeLon != 0.0))
  {
    // Check that the glider position falls inside our window
    // If not then set its position to 0;0 and hide it
    if ((Glider.Latitude <= this.ExtentNorth) && (Glider.Latitude >= this.ExtentSouth) &&
        (Glider.Longitude >= this.ExtentWest) && (Glider.Longitude <= this.ExtentEast))
    {
      Glider.ImgDivElem.style.top = (((this.ExtentNorth - Glider.Latitude) / this.PixelSizeLat) - 28) + "px";
      Glider.ImgDivElem.style.left = (((Glider.Longitude - this.ExtentWest) / this.PixelSizeLon) - 28)+ "px";
      Glider.ImgDivElem.style.visibility = "visible";
      Glider.CycleInfoDiv.style.left = (parseInt(Glider.ImgDivElem.style.left) + 45) + "px";
      Glider.CycleInfoDiv.style.top = (parseInt(Glider.ImgDivElem.style.top) + 45) + "px";
      Glider.CycleInfoDiv.style.visibility = "visible";

      // Figure out where to place the info div element so that it's always on screen when the user
      // moves the mouse over it
      // We default to LR
      Glider.InfoDivElem.style.left = (parseInt(Glider.ImgDivElem.style.left) + 45) + "px";
      Glider.InfoDivElem.style.top = (parseInt(Glider.ImgDivElem.style.top) + 45) + "px";

      // If the glider is too close to the right edge then set div to left hand side
      if ((parseInt(Glider.ImgDivElem.style.left) + 45 + parseInt(Glider.InfoDivElem.style.width)) > parseInt(this.ParentDiv.style.width))
      {
        Glider.InfoDivElem.style.left = (parseInt(Glider.ImgDivElem.style.left) - parseInt(Glider.InfoDivElem.style.width)) + "px";
      }

      // If the glider is too close to the bottom edge then set div to above the glider img
      if ((parseInt(Glider.ImgDivElem.style.top) + 45 + parseInt(Glider.InfoDivElem.style.height)) > parseInt(this.ParentDiv.style.height))
      {
        Glider.InfoDivElem.style.top = (parseInt(Glider.ImgDivElem.style.top) - parseInt(Glider.InfoDivElem.style.height)) + "px";
      }
    }
    else
    {
      Glider.ImgDivElem.style.visibility = "hidden";
      Glider.ImgDivElem.style.top = "0px";
      Glider.ImgDivElem.style.left = "0px";
      Glider.CycleInfoDiv.style.visibility = "hidden";
      Glider.CycleInfoDiv.left = "0px";
      Glider.CycleInfoDiv.top = "0px";
      Glider.InfoDivElem.style.visibility = "hidden";
      Glider.InfoDivElem.left = "0px";
      Glider.InfoDivElem.top = "0px";
    }
  }
}

// ============================================================================
GliderLayerObj.prototype.CalcAndSetGliderImg = function (GliderInfo)
{
 var arVersion = navigator.appVersion.split("MSIE");
 var version = parseFloat(arVersion[1]);
 // Special handling for MSIE < 7
 if ((version >= 5.5) && (version < 7))
    {
    GliderInfo.ImgDivElem.innerHTML = "<img src=\"images/1x1.gif\" style=\"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/glider-red-"+(Math.round(GliderInfo.Heading / 10) * 10)+".png', sizingMethod='image');\">";
    }
 else
    {
    GliderInfo.ImgDivElem.innerHTML = "<img src='images/glider-red-"+(Math.round(GliderInfo.Heading / 10) * 10)+".png'>";
    }
//  GliderInfo.ImgDivElem.style.src = "images/1x1.gif";
//  GliderInfo.ImgDivElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/glider-red-"+(Math.round(GliderInfo.Heading / 10) * 10)+".png', sizingMethod='image');";
}

// ============================================================================
GliderLayerObj.prototype.CycleGliderInfo = function()
{
  var Glider = null;

  for (var i = 0; i < this.Gliders.length; i++)
  {
    Glider = this.Gliders[i];

    switch (this.InfoCycleCounter)
    {
      case 0 : Glider.CycleInfoDiv.innerHTML = "Nick: "+Glider.Nick;
        break;
      case 1 : Glider.CycleInfoDiv.innerHTML = "Name: "+Glider.Name;
        break;
      case 2 : Glider.CycleInfoDiv.innerHTML = "Aircraft: "+Glider.Aircraft;
        break;
      case 3 : Glider.CycleInfoDiv.innerHTML = "Alt: "+Math.round(Glider.Altitude)+"m";
        break;
      case 4 : Glider.CycleInfoDiv.innerHTML = "Cur spd: "+(Math.round(Glider.Speed * 100) / 100) +"km/h";
        break;
      case 5 : Glider.CycleInfoDiv.innerHTML = "Cur hdg: "+(Math.round(Glider.Heading * 100) / 100) +"\u00b0";
    }
  }

  if (this.InfoCycleCounter < 5)
  {
    this.InfoCycleCounter++;
  }
  else
  {
    this.InfoCycleCounter = 0;
  }
}

// ============================================================================
GliderLayerObj.prototype.GliderInfoCycleTimerOnOff = function(OnOffFlag)
{
  // True = on
  if (OnOffFlag)
  {
    this.CycleTimerID = setInterval(this.CycleGliderInfo.bind(this), 3000);
  }
  else
  {
    this.CycleTimerID = clearInterval(this.CycleTimerID);
  }
}

// ============================================================================
GliderObj.prototype.MouseOver = function()
{
  this.InfoDivElem.style.visibility = "visible";
}

// ============================================================================
GliderObj.prototype.MouseOut = function()
{
  this.InfoDivElem.style.visibility = "hidden";
}

