/*
 * Check the order of splice's internal operations, because the ordering is
 * visible externally.
 */

function handlerMaker(expected_exceptions) {
  var order = [];
  function note(trap, name)
  {
      order.push(trap + '-' + name);
      if (expected_exceptions[trap] === name) {
          throw ("fail");
      }
  }

  return [{
    /* this is the only trap we care about */
    deleteProperty: function(target, name) {
      note("del", name);
      return Reflect.deleteProperty(target, name);
    },
    // derived traps
    has:          function(target, name) {
      note("has", name);
      return name in target;
    },
    get:          function(target, name, receiver) {
      note("get", name);
      return Reflect.get(target, name, receiver);
    },
    set:          function(target, name, value, receiver) {
      note("set", name);
      return Reflect.set(target, name, value, receiver);
    },
  }, order];
}

// arr: the array to splice
// expected_order: the expected order of operations on arr, stringified
function check_splice_proxy(arr, expected_order, expected_exceptions, expected_array, expected_result) {
    print (arr);
    var [handler, store] = handlerMaker(expected_exceptions);
    var proxy = new Proxy(arr, handler);

    try {
        var args = Array.prototype.slice.call(arguments, 5);
        var result = Array.prototype.splice.apply(proxy, args);
        assertEq(Object.keys(expected_exceptions).length, 0);
    } catch (e) {
        assertEq(Object.keys(expected_exceptions).length > 0, true);
    }

    // check the order of the property accesses, etc
    assertEq(store.toString(), expected_order);

    // The deleted elements are returned in an object that's always an Array.
    assertEq(Array.isArray(result) || result === undefined, true);

    // check the return value
    for (var i in expected_result) {
        assertEq(result[i], expected_result[i]);
    }
    for (var i in result) {
        assertEq(result[i], expected_result[i]);
    }

    // check the value of arr
    for (var i in expected_array) {
        assertEq(arr[i], expected_array[i]);
    }
    for (var i in arr) {
        assertEq(arr[i], expected_array[i]);
    }

    return result;
}

// Shrinking array
check_splice_proxy(
        [10,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
        "del-5,del-4,del-3," +
        "set-length",
        {},
        [3,4,5],
        [10,1,2],
        0, 3
);

// Growing array
check_splice_proxy(
        [11,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-9,has-4,get-4,set-8,has-3,get-3,set-7," +
        "set-0,set-1,set-2,set-3,set-4,set-5,set-6," +
        "set-length",
        {},
        [9,9,9,9,9,9,9,3,4,5],
        [11,1,2],
        0, 3, 9, 9, 9, 9, 9, 9, 9
);

// Same sized array
check_splice_proxy(
        [12,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "set-0,set-1,set-2," +
        "set-length",
        {},
        [9,9,9,3,4,5],
        [12,1,2],
        0, 3, 9, 9, 9
);


/*
 * Check that if we fail at a particular step in the algorithm, we don't
 * continue with the algorithm beyond that step.
 */


// Step 3: fail when getting length
check_splice_proxy(
        [13,1,2,3,4,5],
        "get-length",
        {get: 'length'},
        [13,1,2,3,4,5],
        undefined,
        0, 3, 9, 9, 9
);

// Step 9b: fail when [[HasProperty]]
check_splice_proxy(
        [14,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1",
        {has: '1'},
        [14,1,2,3,4,5],
        undefined,
        0, 3, 9, 9, 9
);

// Step 9c(i): fail when [[Get]]
check_splice_proxy(
        [15,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1",
        {get: '1'},
        [15,1,2,3,4,5],
        undefined,
        0, 3, 9, 9, 9
);

// Step 12b(iii): fail when [[HasProperty]]
check_splice_proxy(
        [16,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4",
        {has: '4'},
        [3,1,2,3,4,5],
        undefined,
        0, 3
);


// Step 12b(iv)1: fail when [[Get]]
check_splice_proxy(
        [17,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4,get-4",
        {get: '4'},
        [3,1,2,3,4,5],
        undefined,
        0, 3
);


// Step 12b(iv)2: fail when [[Put]]
check_splice_proxy(
        [18,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4,get-4,set-1",
        {set: '1'},
        [3,1,2,3,4,5],
        undefined,
        0, 3
);

// Step 12b(v)1: fail when [[Delete]]
check_splice_proxy(
        [19,1,2,3,,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4,del-1",
        {del: '1'},
        [3,1,2,3,,5],
        undefined,
        0, 3
);

// Step 12d(i): fail when [[Delete]]
check_splice_proxy(
        [20,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
        "del-5,del-4",
        {del: '4'},
        [3,4,5,3,4],
        undefined,
        0, 3
);

// Step 13b(iii): fail when [[HasProperty]]
check_splice_proxy(
        [21,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-8,has-4",
        {has: '4'},
        [21,1,2,3,4,5,,,5],
        undefined,
        0, 3, 9,9,9,9,9,9
);


// Step 13b(iv)1: fail when [[Get]]
check_splice_proxy(
        [22,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-8,has-4,get-4",
        {get: '4'},
        [22,1,2,3,4,5,,,5],
        undefined,
        0, 3, 9,9,9,9,9,9
);


// Step 13b(iv)2: fail when [[Put]]
check_splice_proxy(
        [23,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-8,has-4,get-4,set-7",
        {set: '7'},
        [23,1,2,3,4,5,,,5],
        undefined,
        0, 3, 9,9,9,9,9,9
);

// Step 13b(v)1: fail when [[Delete]]
check_splice_proxy(
        [24,1,2,3,,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-8,has-4,del-7",
        {del: '7'},
        [24,1,2,3,,5,,,5],
        undefined,
        0, 3, 9,9,9,9,9,9
);

// Step 15b: fail when [[Put]]
check_splice_proxy(
        [25,1,2,3,4,5],
        "get-length," +
        "get-constructor," +
        "has-0,get-0,has-1,get-1,has-2,get-2," +
        "has-5,get-5,set-8,has-4,get-4,set-7,has-3,get-3,set-6," +
        "set-0,set-1,set-2",
        {set: '2'},
        [9,9,2,3,4,5,3,4,5],
        undefined,
        0, 3, 9,9,9,9,9,9
);