// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

(function(global, utils) {

"use strict";

%CheckIsBootstrapping();

// -----------------------------------------------------------------------
// Imports

var arrayIterationKindSymbol =
    utils.ImportNow("array_iteration_kind_symbol");
var arrayIteratorNextIndexSymbol =
    utils.ImportNow("array_iterator_next_symbol");
var arrayIteratorObjectSymbol =
    utils.ImportNow("array_iterator_object_symbol");
var GlobalArray = global.Array;
var IteratorPrototype = utils.ImportNow("IteratorPrototype");
var iteratorSymbol = utils.ImportNow("iterator_symbol");
var MakeTypeError;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
var GlobalTypedArray = %object_get_prototype_of(global.Uint8Array);

utils.Import(function(from) {
  MakeTypeError = from.MakeTypeError;
})

// -----------------------------------------------------------------------

function ArrayIterator() {}


// TODO(wingo): Update section numbers when ES6 has stabilized.  The
// section numbers below are already out of date as of the May 2014
// draft.


// 15.4.5.1 CreateArrayIterator Abstract Operation
function CreateArrayIterator(array, kind) {
  var object = TO_OBJECT(array);
  var iterator = new ArrayIterator;
  SET_PRIVATE(iterator, arrayIteratorObjectSymbol, object);
  SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, 0);
  SET_PRIVATE(iterator, arrayIterationKindSymbol, kind);
  return iterator;
}


// 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]
function ArrayIteratorIterator() {
    return this;
}


// ES6 section 22.1.5.2.1 %ArrayIteratorPrototype%.next( )
function ArrayIteratorNext() {
  var iterator = this;
  var value = UNDEFINED;
  var done = true;

  if (!IS_RECEIVER(iterator) ||
      !HAS_DEFINED_PRIVATE(iterator, arrayIteratorNextIndexSymbol)) {
    throw MakeTypeError(kIncompatibleMethodReceiver,
                        'Array Iterator.prototype.next', this);
  }

  var array = GET_PRIVATE(iterator, arrayIteratorObjectSymbol);
  if (!IS_UNDEFINED(array)) {
    var index = GET_PRIVATE(iterator, arrayIteratorNextIndexSymbol);
    var itemKind = GET_PRIVATE(iterator, arrayIterationKindSymbol);
    var length = TO_UINT32(array.length);

    // "sparse" is never used.

    if (index >= length) {
      SET_PRIVATE(iterator, arrayIteratorObjectSymbol, UNDEFINED);
    } else {
      SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);

      if (itemKind == ITERATOR_KIND_VALUES) {
        value = array[index];
      } else if (itemKind == ITERATOR_KIND_ENTRIES) {
        value = [index, array[index]];
      } else {
        value = index;
      }
      done = false;
    }
  }

  return %_CreateIterResultObject(value, done);
}


function ArrayEntries() {
  return CreateArrayIterator(this, ITERATOR_KIND_ENTRIES);
}


function ArrayValues() {
  return CreateArrayIterator(this, ITERATOR_KIND_VALUES);
}


function ArrayKeys() {
  return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
}

// TODO(littledan): Check for detached TypedArray in these three methods
function TypedArrayEntries() {
  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
  return %_Call(ArrayEntries, this);
}


function TypedArrayValues() {
  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
  return %_Call(ArrayValues, this);
}


function TypedArrayKeys() {
  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
  return %_Call(ArrayKeys, this);
}


%FunctionSetPrototype(ArrayIterator, {__proto__: IteratorPrototype});
%FunctionSetInstanceClassName(ArrayIterator, 'Array Iterator');

utils.InstallFunctions(ArrayIterator.prototype, DONT_ENUM, [
  'next', ArrayIteratorNext
]);
utils.SetFunctionName(ArrayIteratorIterator, iteratorSymbol);
%AddNamedProperty(ArrayIterator.prototype, toStringTagSymbol,
                  "Array Iterator", READ_ONLY | DONT_ENUM);

utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
  // No 'values' since it breaks webcompat: http://crbug.com/409858
  'entries', ArrayEntries,
  'keys', ArrayKeys
]);

// TODO(adam): Remove this call once 'values' is in the above
// InstallFunctions block, as it'll be redundant.
utils.SetFunctionName(ArrayValues, 'values');

%AddNamedProperty(GlobalArray.prototype, iteratorSymbol, ArrayValues,
                  DONT_ENUM);

utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [
  'entries', TypedArrayEntries,
  'keys', TypedArrayKeys,
  'values', TypedArrayValues
]);
%AddNamedProperty(GlobalTypedArray.prototype,
                  iteratorSymbol, TypedArrayValues, DONT_ENUM);

// -------------------------------------------------------------------
// Exports

utils.Export(function(to) {
  to.ArrayValues = ArrayValues;
});

%InstallToContext(["array_values_iterator", ArrayValues]);

})