cordova.define("cordova-plugin-googlemaps.BaseArrayClass", function(require, exports, module) {
var utils = require('cordova/utils'),
  BaseClass = require('./BaseClass');

var ARRAY_FIELD = typeof Symbol === 'undefined' ? '__array' + Date.now() : Symbol('array');

var nextTick = function(fn) { Promise.resolve().then(fn); };
/**
 * Create a BaseArrayClass.
 * @class
 */
function BaseArrayClass(array) {
  BaseClass.apply(this);
  var self = this;
  self[ARRAY_FIELD] = [];

  if (array && (array instanceof Array || Array.isArray(array))) {
    for (var i = 0; i < array.length; i++) {
      self[ARRAY_FIELD].push(array[i]);
    }
  }
}

utils.extend(BaseArrayClass, BaseClass);

/**
 * The same as `Array.map` but runs a single async operation at a time.
 *
 * @name mapSeries
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished. Results is an array of the transformed items from holding array.
 * Invoked with (results);
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.mapSeries = function(iteratee, callback) {
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }

  var self = this;

  var results = [];
  var _arrayLength = self[ARRAY_FIELD].length;
  var currentCnt = 0;
  if (_arrayLength === 0) {
    if (typeof callback === 'function') {
      callback.call(self, []);
      return;
    } else {
      return Promise.resolve([]);
    }
  }
  var _looper = function(currentIdx, resolve) {

    iteratee.call(self, self[ARRAY_FIELD][currentIdx], function(value) {
      results[currentIdx] = value;
      currentCnt++;
      if (_arrayLength === currentCnt) {
        resolve(results);
      } else {
        nextTick(function() {
          _looper(currentIdx + 1, resolve);
        });
      }
    });
  };

  return new Promise(function(resolve) {
    nextTick(function() {
      _looper(0, resolve);
    });
  }).then(function(results) {
    if (typeof callback === 'function') {
      return callback.call(self, results);
    } else {
      return Promise.resolve(results);
    }
  });
};


/**
 * The same as `Array.map` but runs async all `iteratee` function at the same time.
 *
 * ```
 * baseArray.mapAsync(function(item, idx, callback) {
 *    ...
 *    callback(value);
 * }).then(function(values) {
 *
 * });
 * ```
 *
 * @name mapAsync
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished. Results is an array of the transformed items from holding array.
 * Invoked with (results);
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.mapAsync = function(iteratee, callback) {
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }
  var self = this;
  var results = [];
  for (var i = 0; i < self[ARRAY_FIELD].length; i++) {
    results.push(null);
  }
  var _arrayLength = self[ARRAY_FIELD].length;
  var finishCnt = 0;
  if (_arrayLength === 0) {
    if (typeof callback === 'function') {
      callback.call(self, []);
      return;
    } else {
      return Promise.resolve([]);
    }
  }

  return new Promise(function(resolve) {
    for (i = 0; i < self[ARRAY_FIELD].length; i++) {
      (function(item, idx) {
        nextTick(function() {
          iteratee.call(self, item, function(value) {
            results[idx] = value;
            finishCnt++;
            if (finishCnt === _arrayLength) {
              resolve(results);
            }
          });
        });
      })(self[ARRAY_FIELD][i], i);
    }
  }).then(function(results) {
    if (typeof callback === 'function') {
      return callback.call(self, results);
    } else {
      return Promise.resolve(results);
    }
  });

};


/**
 * If you provide `iteratee` and `callback`, work as `mapAsync()`.
 * If you provide `iteratee`, but omit `callback`, work as `Array.map()`.
 *
 * @deprecated
 * @name map
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished. Results is an array of the transformed items from holding array.
 * Invoked with (results);
 * @return {Promise|any[]} a promise, if no calback if passed. If you omit `callback`, return transformed items.
 */
BaseArrayClass.prototype.map = function(iteratee, callback) {
  var self = this;

  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }
  if (typeof callback !== 'function') {
    //------------------------
    // example:
    //    var values = baseArray.map(function(item, idx) {
    //       ...
    //       return someValue;
    //    });
    //------------------------
    return self[ARRAY_FIELD].map(iteratee.bind(self));
  }
  return self.mapAsync(iteratee, callback);
};

/**
 * The same as `Array.forEach` but runs async all `iteratee` function at the same time.
 *
 * @name forEachAsync
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished.
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.forEachAsync = function(iteratee, callback) {
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }
  var self = this;
  //------------------------
  // example:
  //    baseArray.forEach(function(item, callback) {
  //       ...
  //       callback();
  //    }, function() {
  //
  //    });
  //------------------------
  var finishCnt = 0;
  var _arrayLength = self[ARRAY_FIELD].length;
  if (_arrayLength === 0) {
    if (typeof callback === 'function') {
      callback.call(self);
      return;
    } else {
      return Promise.resolve();
    }
  }

  return new Promise(function(resolve) {

    self[ARRAY_FIELD].forEach(function(item) {
      iteratee.call(self, item, function() {
        finishCnt++;
        if (finishCnt === _arrayLength) {
          resolve(self);
        }
      });
    });

  })
    .then(function() {
      if (typeof callback === 'function') {
        callback.call(self);
        return;
      } else {
        return Promise.resolve();
      }
    });
};

/**
 * If you provide `iteratee` and `callback`, work as `forEachAsync()`.
 * If you provide `iteratee`, but you omit `callback`, work as `Array.forEach()`
 *
 * @deprecated
 * @name forEach
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished.
 * Invoked with (results);
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.forEach = function(iteratee, callback) {
  var self = this;
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }

  if (typeof callback !== 'function') {
    //------------------------
    // example:
    //    baseArray.forEach(function(item, idx) {
    //       ...
    //    });
    //------------------------
    self[ARRAY_FIELD].forEach(iteratee.bind(self));
    return Promise.resolve();
  }
  return self.forEachAsync(iteratee, callback);
};


/**
 * The same as `Array.filter` but runs async all `iteratee` function at the same time.
 *
 * @name filterAsync
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished. Results is an array of the filtered items from holding array.
 * Invoked with (results);
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.filterAsync = function(iteratee, callback) {
  var self = this;
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }
  //------------------------
  // example:
  //    baseArray.filter(function(item, callback) {
  //       ...
  //       callback(true or false);
  //    }, function(filteredItems) {
  //
  //    });
  //------------------------
  var finishCnt = 0;
  var _arrayLength = self[ARRAY_FIELD].length;
  if (_arrayLength === 0) {
    if (typeof callback === 'function') {
      callback.call(self, []);
      return;
    } else {
      return Promise.resolve([]);
    }
  }
  return (new Promise(function(resolve) {
    var results = [];
    self[ARRAY_FIELD].forEach(function(item, idx) {
      iteratee.call(self, item, function(isOk) {
        results[idx] = isOk;
        finishCnt++;
        if (finishCnt === _arrayLength) {
          resolve(self[ARRAY_FIELD].filter(function(item, j) {
            return results[j];
          }));
        }
      });
    });
  }))
    .then(function(results) {
      if (typeof callback === 'function') {
        callback.call(self, results);
        return;
      } else {
        return Promise.resolve(results);
      }
    });
};

/**
 * If you provide `iteratee` and `callback`, work as `filterAsync()`.
 * If you provide `iteratee`, but you omit `callback`, work as `Array.filter()`
 *
 * @deprecated
 * @name filter
 * @param {Function} iteratee - An async function to apply to each item in array.
 * @param {Function} [callback] - A callback which is called when all `iteratee` functions
 * have finished.
 * @return {Promise} a promise, if no calback if passed.
 */
BaseArrayClass.prototype.filter = function(iteratee, callback) {
  var self = this;
  if (typeof iteratee !== 'function') {
    var error = new Error('iteratee must be a function');
    if (typeof callback === 'function') {
      throw error;
    } else {
      return Promise.reject(error);
    }
  }
  if (typeof callback !== 'function') {
    //------------------------
    // example:
    //    baseArray.filter(function(item, idx) {
    //       ...
    //       return true or false
    //    });
    //------------------------
    return self[ARRAY_FIELD].filter(iteratee);
  }
  return self.filterAsync(iteratee, callback);
};

/**
 * Returns the first index at which a given element can be found in the array, or -1 if it is not present.
 *
 * @name indexOf
 * @param {any} searchElement - Element to locate in the array.
 * @param {number} [searchElement] - The index to start the search at.
 * If the index is greater than or equal to the array's length, -1 is returned,
 * which means the array will not be searched.
 * If the provided index value is a negative number,
 * it is taken as the offset from the end of the array.
 * Note: if the provided index is negative, the array is still searched from front to back.
 * If the provided index is 0, then the whole array will be searched. Default: 0 (entire array is searched).
 * @return The first index of the element in the array; -1 if not found.
 */
BaseArrayClass.prototype.indexOf = function(item, searchElement) {
  searchElement = searchElement === undefined || searchElement === null ? 0 : searchElement;
  if (typeof searchElement !== 'number') {
    throw new Error('searchElement must be a number');
  }
  if (searchElement < 0) {
    throw new Error('searchElement must be over number than 0');
  }

  return this[ARRAY_FIELD].indexOf(item, searchElement);
};

/**
 * Removes all elements. Fire `remove_at` event for each element.
 *
 * @name empty
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event.
 */
BaseArrayClass.prototype.empty = function(noNotify) {
  var self = this;
  var cnt = self[ARRAY_FIELD].length;
  for (var i = cnt - 1; i >= 0; i--) {
    self.removeAt(i, noNotify);
  }
};

/**
 * Adds one element to the end of an array and returns the new length of the array.
 * Fire `insert_at` event if `noNotify` is `false`.
 *
 * @name push
 * @param {any} value - The element to add to the end of the array.
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `insert_at` event.
 * @return {number} The new length property of the object upon which the method was called.
 */
BaseArrayClass.prototype.push = function(value, noNotify) {
  var self = this;
  self[ARRAY_FIELD].push(value);
  if (noNotify !== true) {
    self.trigger('insert_at', self[ARRAY_FIELD].length - 1);
  }
  return self[ARRAY_FIELD].length;
};

/**
 * Adds one element to the end of an array and returns the new length of the array.
 * Fire `insert_at` event if `noNotify` is `false`.
 *
 * @name insertAt
 * @param {number} index - The position of the array you want to insert new element.
 * @param {any} value - The element to add to the end of the array.
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `insert_at` event.
 * @return {number} The new length property of the object upon which the method was called.
 */
BaseArrayClass.prototype.insertAt = function(index, value, noNotify) {
  var self = this;
  if (typeof index !== 'number') {
    throw new Error('index must be a number');
  }
  if (index < 0) {
    throw new Error('index must be over number than 0');
  }
  self[ARRAY_FIELD].splice(index, 0, value);
  if (noNotify !== true) {
    self.trigger('insert_at', index);
  }
  return self[ARRAY_FIELD].length;
};

/**
 * Returns a new array that is the clone of internal array.
 *
 * @name getArray
 * @return {Array<any>} New array
 */
BaseArrayClass.prototype.getArray = function() {
  //return _array.slice(0);  <-- Android browser keeps the same instance of original array
  return JSON.parse(JSON.stringify(this[ARRAY_FIELD]));
};

/**
 * Returns item of specified position.
 *
 * @name getAt
 * @param {number} index - The position of the array you want to get.
 * @return {any} item
 */
BaseArrayClass.prototype.getAt = function(index) {
  var self = this;
  if (typeof index !== 'number') {
    throw new Error('index must be a number');
  }
  if (index < 0) {
    throw new Error('index must be over number than 0');
  }
  if (index >= self[ARRAY_FIELD].length) {
    throw new Error('index must be lower number than ' + self[ARRAY_FIELD].length);
  }
  return this[ARRAY_FIELD][index];
};

/**
 * Replaces item of specified position.
 *
 * @name setAt
 * @param {number} index - The position of the array you want to get.
 * @param {any} value - New element
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `set_at` event.
 * @return {any} previous item
 */
BaseArrayClass.prototype.setAt = function(index, value, noNotify) {
  var self = this;
  if (typeof index !== 'number') {
    throw new Error('index must be a number');
  }
  if (index < 0) {
    throw new Error('index must be over number than 0');
  }
  if (index > self[ARRAY_FIELD].length) {
    for (var i = self[ARRAY_FIELD].length; i <= index; i++) {
      self[ARRAY_FIELD][i] = undefined;
    }
  }
  var prev = self[ARRAY_FIELD][index];
  self[ARRAY_FIELD][index] = value;
  if (noNotify !== true) {
    self.trigger('set_at', index, prev);
  }
  return prev;
};

/**
 * Removes item of specified position.
 *
 * @name removeAt
 * @param {number} index - The position of the array you want to get.
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event.
 * @return {any} removed item
 */
BaseArrayClass.prototype.removeAt = function(index, noNotify) {
  var self = this;
  if (typeof index !== 'number') {
    throw new Error('index must be a number');
  }
  if (index < 0) {
    throw new Error('index must be over number than 0');
  }
  if (index >= self[ARRAY_FIELD].length) {
    throw new Error('index must be lower number than ' + self[ARRAY_FIELD].length);
  }
  var value = self[ARRAY_FIELD][index];
  self[ARRAY_FIELD].splice(index, 1);
  if (noNotify !== true) {
    self.trigger('remove_at', index, value);
  }
  return value;
};

/**
 * Removes item of the last array item.
 *
 * @name pop
 * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event.
 * @return {any} removed item
 */
BaseArrayClass.prototype.pop = function(noNotify) {
  var self = this;
  var index = self[ARRAY_FIELD].length - 1;
  var value = self[ARRAY_FIELD].pop();
  if (noNotify !== true) {
    self.trigger('remove_at', index, value);
  }
  return value;
};

/**
 * Returns the length of array.
 *
 * @name getLength
 * @return {any} Number of items
 */
BaseArrayClass.prototype.getLength = function() {
  return parseInt(this[ARRAY_FIELD].length, 10); // In order to prevent manupulating through `length` property, conver to number mandatory using `parseInt`.
};

/**
 * Reverses an array in place. The first array element becomes the last, and the last array element becomes the first.
 *
 * @name reverse
 */
BaseArrayClass.prototype.reverse = function() {
  this[ARRAY_FIELD].reverse();
};

/**
 * The `sort()` method sorts the elements of an array in place and returns the array.
 * The same as `array.sort()`.
 *
 * @name sort
 * @param {Function} [compareFunction] - Specifies a function that defines the sort order.
 *  If omitted, the array is sorted according to each character's Unicode code point value,
 *  according to the string conversion of each element.
 */
BaseArrayClass.prototype.sort = function(compareFunction) {
  if (typeof compareFunction === 'function') {
    this[ARRAY_FIELD].sort(compareFunction);
  } else {
    this[ARRAY_FIELD].sort();
  }
};


module.exports = BaseArrayClass;

});
