'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _index = require('./index.js');

var _index2 = _interopRequireDefault(_index);

var _geolocationproperty = require('./geolocationproperty.js');

var _geolocationproperty2 = _interopRequireDefault(_geolocationproperty);

var _object = require('./object.js');

var _object2 = _interopRequireDefault(_object);

var _sphere = require('./sphere.js');

var _sphere2 = _interopRequireDefault(_sphere);

var _events = require('./events.js');

var _events2 = _interopRequireDefault(_events);

var _eventtype = require('./events/eventtype.js');

var _eventtype2 = _interopRequireDefault(_eventtype);

var _polygon = require('./geom/polygon.js');

var _polygon2 = _interopRequireDefault(_polygon);

var _has = require('./has.js');

var _has2 = _interopRequireDefault(_has);

var _math = require('./math.js');

var _math2 = _interopRequireDefault(_math);

var _proj = require('./proj.js');

var _proj2 = _interopRequireDefault(_proj);

var _epsg = require('./proj/epsg4326.js');

var _epsg2 = _interopRequireDefault(_epsg);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

/**
 * @classdesc
 * Helper class for providing HTML5 Geolocation capabilities.
 * The [Geolocation API](http://www.w3.org/TR/geolocation-API/)
 * is used to locate a user's position.
 *
 * To get notified of position changes, register a listener for the generic
 * `change` event on your instance of `ol.Geolocation`.
 *
 * Example:
 *
 *     var geolocation = new ol.Geolocation({
 *       // take the projection to use from the map's view
 *       projection: view.getProjection()
 *     });
 *     // listen to changes in position
 *     geolocation.on('change', function(evt) {
 *       window.console.log(geolocation.getPosition());
 *     });
 *
 * @fires error
 * @constructor
 * @extends {ol.Object}
 * @param {olx.GeolocationOptions=} opt_options Options.
 * @api
 */
var _ol_Geolocation_ = function (opt_options) {

  _object2.default.call(this);

  var options = opt_options || {};

  /**
   * The unprojected (EPSG:4326) device position.
   * @private
   * @type {ol.Coordinate}
   */
  this.position_ = null;

  /**
   * @private
   * @type {ol.TransformFunction}
   */
  this.transform_ = _proj2.default.identityTransform;

  /**
   * @private
   * @type {ol.Sphere}
   */
  this.sphere_ = new _sphere2.default(_epsg2.default.RADIUS);

  /**
   * @private
   * @type {number|undefined}
   */
  this.watchId_ = undefined;

  _events2.default.listen(this, _object2.default.getChangeEventType(_geolocationproperty2.default.PROJECTION), this.handleProjectionChanged_, this);
  _events2.default.listen(this, _object2.default.getChangeEventType(_geolocationproperty2.default.TRACKING), this.handleTrackingChanged_, this);

  if (options.projection !== undefined) {
    this.setProjection(options.projection);
  }
  if (options.trackingOptions !== undefined) {
    this.setTrackingOptions(options.trackingOptions);
  }

  this.setTracking(options.tracking !== undefined ? options.tracking : false);
}; // FIXME handle geolocation not supported

_index2.default.inherits(_ol_Geolocation_, _object2.default);

/**
 * @inheritDoc
 */
_ol_Geolocation_.prototype.disposeInternal = function () {
  this.setTracking(false);
  _object2.default.prototype.disposeInternal.call(this);
};

/**
 * @private
 */
_ol_Geolocation_.prototype.handleProjectionChanged_ = function () {
  var projection = this.getProjection();
  if (projection) {
    this.transform_ = _proj2.default.getTransformFromProjections(_proj2.default.get('EPSG:4326'), projection);
    if (this.position_) {
      this.set(_geolocationproperty2.default.POSITION, this.transform_(this.position_));
    }
  }
};

/**
 * @private
 */
_ol_Geolocation_.prototype.handleTrackingChanged_ = function () {
  if (_has2.default.GEOLOCATION) {
    var tracking = this.getTracking();
    if (tracking && this.watchId_ === undefined) {
      this.watchId_ = navigator.geolocation.watchPosition(this.positionChange_.bind(this), this.positionError_.bind(this), this.getTrackingOptions());
    } else if (!tracking && this.watchId_ !== undefined) {
      navigator.geolocation.clearWatch(this.watchId_);
      this.watchId_ = undefined;
    }
  }
};

/**
 * @private
 * @param {GeolocationPosition} position position event.
 */
_ol_Geolocation_.prototype.positionChange_ = function (position) {
  var coords = position.coords;
  this.set(_geolocationproperty2.default.ACCURACY, coords.accuracy);
  this.set(_geolocationproperty2.default.ALTITUDE, coords.altitude === null ? undefined : coords.altitude);
  this.set(_geolocationproperty2.default.ALTITUDE_ACCURACY, coords.altitudeAccuracy === null ? undefined : coords.altitudeAccuracy);
  this.set(_geolocationproperty2.default.HEADING, coords.heading === null ? undefined : _math2.default.toRadians(coords.heading));
  if (!this.position_) {
    this.position_ = [coords.longitude, coords.latitude];
  } else {
    this.position_[0] = coords.longitude;
    this.position_[1] = coords.latitude;
  }
  var projectedPosition = this.transform_(this.position_);
  this.set(_geolocationproperty2.default.POSITION, projectedPosition);
  this.set(_geolocationproperty2.default.SPEED, coords.speed === null ? undefined : coords.speed);
  var geometry = _polygon2.default.circular(this.sphere_, this.position_, coords.accuracy);
  geometry.applyTransform(this.transform_);
  this.set(_geolocationproperty2.default.ACCURACY_GEOMETRY, geometry);
  this.changed();
};

/**
 * Triggered when the Geolocation returns an error.
 * @event error
 * @api
 */

/**
 * @private
 * @param {GeolocationPositionError} error error object.
 */
_ol_Geolocation_.prototype.positionError_ = function (error) {
  error.type = _eventtype2.default.ERROR;
  this.setTracking(false);
  this.dispatchEvent( /** @type {{type: string, target: undefined}} */error);
};

/**
 * Get the accuracy of the position in meters.
 * @return {number|undefined} The accuracy of the position measurement in
 *     meters.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getAccuracy = function () {
  return (
    /** @type {number|undefined} */this.get(_geolocationproperty2.default.ACCURACY)
  );
};

/**
 * Get a geometry of the position accuracy.
 * @return {?ol.geom.Polygon} A geometry of the position accuracy.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getAccuracyGeometry = function () {
  return (
    /** @type {?ol.geom.Polygon} */this.get(_geolocationproperty2.default.ACCURACY_GEOMETRY) || null
  );
};

/**
 * Get the altitude associated with the position.
 * @return {number|undefined} The altitude of the position in meters above mean
 *     sea level.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getAltitude = function () {
  return (
    /** @type {number|undefined} */this.get(_geolocationproperty2.default.ALTITUDE)
  );
};

/**
 * Get the altitude accuracy of the position.
 * @return {number|undefined} The accuracy of the altitude measurement in
 *     meters.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getAltitudeAccuracy = function () {
  return (
    /** @type {number|undefined} */this.get(_geolocationproperty2.default.ALTITUDE_ACCURACY)
  );
};

/**
 * Get the heading as radians clockwise from North.
 * @return {number|undefined} The heading of the device in radians from north.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getHeading = function () {
  return (
    /** @type {number|undefined} */this.get(_geolocationproperty2.default.HEADING)
  );
};

/**
 * Get the position of the device.
 * @return {ol.Coordinate|undefined} The current position of the device reported
 *     in the current projection.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getPosition = function () {
  return (
    /** @type {ol.Coordinate|undefined} */this.get(_geolocationproperty2.default.POSITION)
  );
};

/**
 * Get the projection associated with the position.
 * @return {ol.proj.Projection|undefined} The projection the position is
 *     reported in.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getProjection = function () {
  return (
    /** @type {ol.proj.Projection|undefined} */this.get(_geolocationproperty2.default.PROJECTION)
  );
};

/**
 * Get the speed in meters per second.
 * @return {number|undefined} The instantaneous speed of the device in meters
 *     per second.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getSpeed = function () {
  return (
    /** @type {number|undefined} */this.get(_geolocationproperty2.default.SPEED)
  );
};

/**
 * Determine if the device location is being tracked.
 * @return {boolean} The device location is being tracked.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getTracking = function () {
  return (
    /** @type {boolean} */this.get(_geolocationproperty2.default.TRACKING)
  );
};

/**
 * Get the tracking options.
 * @see http://www.w3.org/TR/geolocation-API/#position-options
 * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by
 *     the [HTML5 Geolocation spec
 *     ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.getTrackingOptions = function () {
  return (
    /** @type {GeolocationPositionOptions|undefined} */this.get(_geolocationproperty2.default.TRACKING_OPTIONS)
  );
};

/**
 * Set the projection to use for transforming the coordinates.
 * @param {ol.ProjectionLike} projection The projection the position is
 *     reported in.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.setProjection = function (projection) {
  this.set(_geolocationproperty2.default.PROJECTION, _proj2.default.get(projection));
};

/**
 * Enable or disable tracking.
 * @param {boolean} tracking Enable tracking.
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.setTracking = function (tracking) {
  this.set(_geolocationproperty2.default.TRACKING, tracking);
};

/**
 * Set the tracking options.
 * @see http://www.w3.org/TR/geolocation-API/#position-options
 * @param {GeolocationPositionOptions} options PositionOptions as defined by the
 *     [HTML5 Geolocation spec
 *     ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
 * @observable
 * @api
 */
_ol_Geolocation_.prototype.setTrackingOptions = function (options) {
  this.set(_geolocationproperty2.default.TRACKING_OPTIONS, options);
};
exports.default = _ol_Geolocation_;
module.exports = exports['default'];