cordova.define("cordova-plugin-googlemaps.commandQueueExecutor", function(require, exports, module) {
/*****************************************************************************
 * Command queue mechanism
 * (Save the number of method executing at the same time)
 *****************************************************************************/
var cordova_exec = require('cordova/exec'),
  common = require('./Common');

var commandQueue = [];
var _isWaitMethod = null;
var _isExecuting = false;
var _executingCnt = 0;
var MAX_EXECUTE_CNT = 10;
var _lastGetMapExecuted = 0;
var _isResizeMapExecuting = false;

// This flag becomes true when the page will be unloaded.
var _stopRequested = false;


function execCmd(success, error, pluginName, methodName, args, execOptions) {
  execOptions = execOptions || {};

  // The JavaScript special keyword 'this' indicates `who call this function`.
  // This execCmd function is executed from overlay instances such as marker.
  // So `this` means `overlay` instance.
  var overlay = this;

  // If the overlay has been already removed from map,
  // do not execute any methods on it.
  if (overlay._isRemoved && !execOptions.remove) {
    console.error('[ignore]' + pluginName + '.' + methodName + ', because removed.');
    return true;
  }

  // If the overlay is not ready in native side,
  // do not execute any methods except remove on it.
  // This code works for map class especially.
  if (!this._isReady && methodName !== 'remove') {
    console.error('[ignore]' + pluginName + '.' + methodName + ', because it\'s not ready.');
    return true;
  }

  // Push the method into the commandQueue(FIFO) at once.
  commandQueue.push({
    'execOptions': execOptions,
    'args': [
      function() {
        //-------------------------------
        // success callback
        //-------------------------------

        if (methodName === 'resizeMap') {
          _isResizeMapExecuting = false;
        }

        // Even if the method was successful,
        // but the _stopRequested flag is true,
        // do not execute further code.
        if (!_stopRequested && success) {
          var results = Array.prototype.slice.call(arguments, 0);
          common.nextTick(function() {
            success.apply(overlay,results);
          });
        }

        // Insert small delays only for `map.getMap()`,
        // because if you execute three or four `map.getMap()` at the same time,
        // Android OS itself crashes.
        // In order to prevent this error, insert small delays.
        var delay = 0;
        if (methodName === _isWaitMethod) {
          if (_isWaitMethod === 'getMap' && Date.now() - _lastGetMapExecuted < 1500) {
            delay = 1500;
          }
          _lastGetMapExecuted = Date.now();
          _isWaitMethod = null;
        }
        setTimeout(function() {
          _executingCnt--;
          common.nextTick(_exec);
        }, delay);
      },
      function() {
        //-------------------------------
        // error callback
        //-------------------------------
        if (methodName === 'resizeMap') {
          _isResizeMapExecuting = false;
        }
        if (!_stopRequested && error) {
          var results = Array.prototype.slice.call(arguments, 0);
          common.nextTick(function() {
            error.apply(overlay,results);
          });
        }

        if (methodName === _isWaitMethod) {
          _isWaitMethod = null;
        }
        _executingCnt--;
        common.nextTick(_exec);
      },
      pluginName, methodName, args]
  });

  // In order to execute all methods in safe,
  // the maps plugin limits the number of execution in a moment to 10.
  //
  // Note that Cordova-Android drops has also another internal queue,
  // and the internal queue drops our statement if the app send too much.
  //
  // Also executing too much statements at the same time,
  // it would cause many errors in native side, such as out-of-memory.
  //
  // In order to prevent these troubles, the maps plugin limits the number of execution is 10.
  if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) {
    return;
  }
  common.nextTick(_exec);
}
function _exec() {

  // You probably wonder why there is this code because it's already simular code at the end of the execCmd function.
  //
  // Because the commandQueue might change after the last code of the execCmd.
  // (And yes, it was occurred.)
  // In order to block surely, block the execution again.
  if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) {
    return;
  }
  _isExecuting = true;

  // Execute some execution requests (up to 10) from the commandQueue.
  var methodName;
  while (commandQueue.length > 0 && _executingCnt < MAX_EXECUTE_CNT) {
    if (!_stopRequested) {
      _executingCnt++;
    }

    // Pick up the head one.
    var commandParams = commandQueue.shift();
    methodName = commandParams.args[3];

    // If the request is `map.refreshLayout()` and another `map.refreshLayout()` is executing,
    // skip it.
    // This prevents to execute multiple `map.refreshLayout()` at the same time.
    if (methodName === 'resizeMap') {
      if (_isResizeMapExecuting) {
        _executingCnt--;
        continue;
      }
      _isResizeMapExecuting = true;
    }

    // If the `_stopRequested` flag is true,
    // do not execute any statements except `remove()` or `clear()` methods.
    if (_stopRequested && (!commandParams.execOptions.remove || methodName !== 'clear')) {
      _executingCnt--;
      continue;
    }

    // Some methods have to block other execution requests, such as `map.clear()`
    if (commandParams.execOptions.sync) {
      _isWaitMethod = methodName;
      cordova_exec.apply(this, commandParams.args);
      break;
    }
    cordova_exec.apply(this, commandParams.args);
  }

  _isExecuting = false;

}


//----------------------------------------------------
// Stop all executions if the page will be closed.
//----------------------------------------------------
function stopExecution() {
  // Request stop all tasks.
  _stopRequested = true;
}
window.addEventListener('unload', stopExecution);

module.exports = execCmd;

});
