text: /*
   *
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   *
   */
  
  /**
   * This class provides access to device accelerometer data.
   * @constructor
   */
  var argscheck = require("cordova/argscheck"),
    utils = require("cordova/utils"),
    exec = require("cordova/exec"),
    Acceleration = require("./Acceleration");
  
  // Is the accel sensor running?
  var running = false;
  
  // Keeps reference to watchAcceleration calls.
  var timers = {};
  
  // Array of listeners; used to keep track of when we should call start and stop.
  var listeners = [];
  
  // Last returned acceleration object from native
  var accel = null;
  
  // Timer used when faking up devicemotion events
  var eventTimerId = null;
  
  // Tells native to start.
  function start() {
    exec(
      function(a) {
        var tempListeners = listeners.slice(0);
        accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
        for (var i = 0, l = tempListeners.length; i < l; i++) {
          tempListeners[i].win(accel);
        }
      },
      function(e) {
        var tempListeners = listeners.slice(0);
        for (var i = 0, l = tempListeners.length; i < l; i++) {
          tempListeners[i].fail(e);
        }
      },
      "Accelerometer",
      "start",
      []
    );
    running = true;
  }
  
  // Tells native to stop.
  function stop() {
    exec(null, null, "Accelerometer", "stop", []);
    accel = null;
    running = false;
  }
  
  // Adds a callback pair to the listeners array
  function createCallbackPair(win, fail) {
    return { win: win, fail: fail };
  }
  
  // Removes a win/fail listener pair from the listeners array
  function removeListeners(l) {
    var idx = listeners.indexOf(l);
    if (idx > -1) {
      listeners.splice(idx, 1);
      if (listeners.length === 0) {
        stop();
      }
    }
  }
  
  var accelerometer = {
    /**
     * Asynchronously acquires the current acceleration.
     *
     * @param {Function} successCallback    The function to call when the acceleration data is available
     * @param {Function} errorCallback      The function to call when there is an error getting the acceleration data. (OPTIONAL)
     * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
     */
    getCurrentAcceleration: function(successCallback, errorCallback, options) {
      argscheck.checkArgs(
        "fFO",
        "accelerometer.getCurrentAcceleration",
        arguments
      );
  
      if (cordova.platformId === "windowsphone") {
        exec(
          function(a) {
            accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
            successCallback(accel);
          },
          function(e) {
            errorCallback(e);
          },
          "Accelerometer",
          "getCurrentAcceleration",
          []
        );
  
        return;
      }
  
      if (cordova.platformId === "browser" && !eventTimerId) {
        // fire devicemotion event once
        var devicemotionEvent = new Event("devicemotion");
        window.setTimeout(function() {
          window.dispatchEvent(devicemotionEvent);
        }, 200);
      }
  
      var p;
      var win = function(a) {
        removeListeners(p);
        successCallback(a);
      };
      var fail = function(e) {
        removeListeners(p);
        if (errorCallback) {
          errorCallback(e);
        }
      };
  
      p = createCallbackPair(win, fail);
      listeners.push(p);
  
      if (!running) {
        start();
      }
    },
  
    /**
     * Asynchronously acquires the acceleration repeatedly at a given interval.
     *
     * @param {Function} successCallback    The function to call each time the acceleration data is available
     * @param {Function} errorCallback      The function to call when there is an error getting the acceleration data. (OPTIONAL)
     * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
     * @return String                       The watch id that must be passed to #clearWatch to stop watching.
     */
    watchAcceleration: function(successCallback, errorCallback, options) {
      argscheck.checkArgs("fFO", "accelerometer.watchAcceleration", arguments);
      // Default interval (10 sec)
      var frequency =
        options && options.frequency && typeof options.frequency == "number"
          ? options.frequency
          : 10000;
  
      // Keep reference to watch id, and report accel readings as often as defined in frequency
      var id = utils.createUUID();
  
      var p = createCallbackPair(
        function() {},
        function(e) {
          removeListeners(p);
          if (errorCallback) {
            errorCallback(e);
          }
        }
      );
      listeners.push(p);
  
      timers[id] = {
        timer: window.setInterval(function() {
          if (accel) {
            successCallback(accel);
          }
        }, frequency),
        listeners: p
      };
  
      if (running) {
        // If we're already running then immediately invoke the success callback
        // but only if we have retrieved a value, sample code does not check for null ...
        if (accel) {
          successCallback(accel);
        }
      } else {
        start();
      }
  
      if (cordova.platformId === "browser" && !eventTimerId) {
        // Start firing devicemotion events if we haven't already
        var devicemotionEvent = new Event("devicemotion");
        eventTimerId = window.setInterval(function() {
          window.dispatchEvent(devicemotionEvent);
        }, 200);
      }
  
      return id;
    },
  
    /**
     * Clears the specified accelerometer watch.
     *
     * @param {String} id       The id of the watch returned from #watchAcceleration.
     */
    clearWatch: function(id) {
      // Stop javascript timer & remove from timer list
      if (id && timers[id]) {
        window.clearInterval(timers[id].timer);
        removeListeners(timers[id].listeners);
        delete timers[id];
  
        if (eventTimerId && Object.keys(timers).length === 0) {
          // No more watchers, so stop firing 'devicemotion' events
          window.clearInterval(eventTimerId);
          eventTimerId = null;
        }
      }
    }
  };
  module.exports = accelerometer;
  