text: 'use strict';
  
  /*global require,Int16Array,Uint8Array,Int16Array*/
  var CesiumMath = require('terriajs-cesium/Source/Core/Math');
  var defined = require('terriajs-cesium/Source/Core/defined');
  var loadArrayBuffer = require('../Core/loadArrayBuffer');
  var when = require('terriajs-cesium/Source/ThirdParty/when');
  
  /**
   * The Earth Gravity Model 1996 (EGM96) geoid.
   * @param {String} gridFileUrl The URL of the WW15MGH.DAC file.
   */
  var EarthGravityModel1996 = function(gridFileUrl) {
      this.gridFileUrl = gridFileUrl;
      this.data = undefined;
  
      // These values were determined by inspecting the WW15MGH.DAC file.  We hard-code them here because
      // we need them available before that file finishes loading.
      this.minimumHeight = -106.99;
      this.maximumHeight = 85.39;
  };
  
  /**
   * Determines if this class will work in the current environment.  It will return false on older browsers without support
   * for typed arrays.
   * @return {Boolean} True if this class may be used in this environment; otherwise, false.
   */
  EarthGravityModel1996.isSupported = function() {
      return typeof Int16Array !== 'undefined' && typeof Uint8Array !== 'undefined';
  };
  
  /**
   * Gets the height of EGM96 above the surface of the ellipsoid.
   * @param {String} baseUrl The base URL for TerriaJS resources.
   * @param {Number} longitude The longitude.
   * @param {Number} latitude The latitude
   * @return {Promise|Number} A promise, that, when it results The height of mean sea level above the ellipsoid at the specified location.  Negative numbers indicate that mean sea level
   *                  is below the ellipsoid.
   */
  EarthGravityModel1996.prototype.getHeight = function(longitude, latitude) {
      return getHeightData(this).then(function(data) {
          return getHeightFromData(data, longitude, latitude);
      });
  };
  
  EarthGravityModel1996.prototype.getHeights = function(cartographicArray) {
      return getHeightData(this).then(function(data) {
          for (var i = 0; i < cartographicArray.length; ++i) {
              var cartographic = cartographicArray[i];
              cartographic.height = getHeightFromData(data, cartographic.longitude, cartographic.latitude);
          }
          return cartographicArray;
      });
  };
  
  function getHeightData(model) {
      if (!defined(model.data)) {
          model.data = loadArrayBuffer(model.gridFileUrl);
      }
  
      return when(model.data, function(data) {
          if (!(model.data instanceof Int16Array)) {
              // Data file is big-endian, all relevant platforms are little endian, so swap the byte order.
              var byteView = new Uint8Array(data);
              for (var k = 0; k < byteView.length; k += 2) {
                  var tmp = byteView[k];
                  byteView[k] = byteView[k + 1];
                  byteView[k + 1] = tmp;
              }
              model.data = new Int16Array(data);
          }
  
          return model.data;
      });
  }
  
  function getHeightFromData(data, longitude, latitude) {
      var recordIndex = 720 * (CesiumMath.PI_OVER_TWO - latitude) / Math.PI;
      if (recordIndex < 0) {
          recordIndex = 0;
      } else if (recordIndex > 720) {
          recordIndex = 720;
      }
  
      longitude = CesiumMath.zeroToTwoPi(longitude);
      var heightIndex = 1440 * longitude / CesiumMath.TWO_PI;
      if (heightIndex < 0) {
          heightIndex = 0;
      } else if (heightIndex > 1440) {
          heightIndex = 1440;
      }
  
      var i = heightIndex | 0;
      var j = recordIndex | 0;
  
      var xMinusX1 = heightIndex - i;
      var yMinusY1 = recordIndex - j;
      var x2MinusX = 1.0 - xMinusX1;
      var y2MinusY = 1.0 - yMinusY1;
  
      var f11 = getHeightValue(data, j, i);
      var f21 = getHeightValue(data, j, i + 1);
      var f12 = getHeightValue(data, j + 1, i);
      var f22 = getHeightValue(data, j + 1, i + 1);
  
      return (f11 * x2MinusX * y2MinusY + f21 * xMinusX1 * y2MinusY + f12 * x2MinusX * yMinusY1 + f22 * xMinusX1 * yMinusY1) / 100.0;
  }
  
  // Heights returned by this function are in centimeters.
  function getHeightValue(data, recordIndex, heightIndex) {
      if (recordIndex > 720) {
          recordIndex = 720;
      } else if (recordIndex < 0) {
          recordIndex = 0;
      }
  
      if (heightIndex > 1439) {
          heightIndex -= 1440;
      } else if (heightIndex < 0) {
          heightIndex += 1440;
      }
  
      return data[recordIndex * 1440 + heightIndex];
  }
  
  module.exports = EarthGravityModel1996;
  