'use strict';

var ArrayList = require('dw/util/ArrayList');

/**
 * Map method for dw.util.Collection subclass instance
 * @param {dw.util.Collection} collection - Collection subclass instance to map over
 * @param {Function} callback - Callback function for each item
 * @param {Object} [scope] - Optional execution scope to pass to callback
 * @returns {Array} Array of results of map
 */
function map(collection, callback, scope) {
    var iterator = Object.hasOwnProperty.call(collection, 'iterator')
        ? collection.iterator()
        : collection;
    var index = 0;
    var item = null;
    var result = [];
    while (iterator.hasNext()) {
        item = iterator.next();
        result.push(scope ? callback.call(scope, item, index, collection)
            : callback(item, index, collection));
        index++;
    }
    return result;
}

/**
 * forEach method for dw.util.Collection subclass instances
 * @param {dw.util.Collection} collection - Collection subclass instance to map over
 * @param {Function} callback - Callback function for each item
 * @param {Object} [scope] - Optional execution scope to pass to callback
 * @returns {void}
 */
function forEach(collection, callback, scope) {
    var iterator = collection.iterator();
    var index = 0;
    var item = null;
    while (iterator.hasNext()) {
        item = iterator.next();
        if (scope) {
            callback.call(scope, item, index, collection);
        } else {
            callback(item, index, collection);
        }
        index++;
    }
}

/**
 * concat method for dw.util.Collection subclass instances
 * @param  {...dw.util.Collection} arguments - first collection to concatinate
 * @return {dw.util.ArrayList} ArrayList containing all passed collections
 */
function concat() {
    var result = new ArrayList();
    for (var i = 0, l = arguments.length; i < l; i++) {
        result.addAll(arguments[i]);
    }
    return result;
}

/**
 * reduce method for dw.util.Collection subclass instances
 * @param {dw.util.Collection} collection - Collection subclass instance to reduce
 * @param {Function} callback - Function to execute on each value in the array
 * @return {Object} result of the execution of callback function on all items
 */
function reduce(collection, callback) {
    if (typeof callback !== 'function') {
        throw new TypeError(callback + ' is not a function');
    }

    var value;
    var index = 1;
    var iterator = collection.iterator();

    if (arguments.length === 3) {
        value = arguments[2];
        index = 0;
    } else if (iterator.hasNext() && (collection.getLength() !== 1)) {
        value = iterator.next();
    }

    if (collection.getLength() === 0 && !value) {
        throw new TypeError('Reduce of empty array with no initial value');
    }

    if ((collection.getLength() === 1 && !value) || (collection.getLength() === 0 && value)) {
        return collection.getLength() === 1 ? iterator.next() : value;
    }

    while (iterator.hasNext()) {
        var item = iterator.next();
        value = callback(value, item, index, collection);
        index++;
    }

    return value;
}

/**
 * Pluck method for dw.util.Collection subclass instance
 * @param {dw.util.Collection|dw.util.Iterator} list - Collection subclass or Iterator instance to
 *     pluck from
 * @param {string} property - Object property to pluck
 * @returns {Array} Array of results of plucked properties
 */
function pluck(list, property) {
    var result = [];
    var iterator = Object.hasOwnProperty.call(list, 'iterator') ? list.iterator() : list;
    while (iterator.hasNext()) {
        var temp = iterator.next();
        if (temp[property]) {
            result.push(temp[property]);
        }
    }
    return result;
}

/**
 * Find method for dw.util.Collection subclass instance
 * @param {dw.util.Collection} collection - Collection subclass instance to find value in
 * @param {Function} match - Match function
 * @param {Object} [scope] - Optional execution scope to pass to the match function
 * @returns {Object|null} Single item from the collection
 */
function find(collection, match, scope) {
    var result = null;

    if (collection) {
        var iterator = collection.iterator();
        while (iterator.hasNext()) {
            var item = iterator.next();
            if (scope ? match.call(scope, item) : match(item)) {
                result = item;
                break;
            }
        }
    }

    return result;
}

/**
 * Gets the first item from dw.util.Collection subclass instance
 * @param {dw.util.Colleciton} collection - Collection subclass instance to work with
 * @return {Object|null} First element from the collection
 */
function first(collection) {
    var iterator = collection.iterator();
    return iterator.hasNext() ? iterator.next() : null;
}

/**
 * Determines whether every list item meets callback's truthy conditional
 *
 * @param {dw.util.Collection} collection - Collection subclass instance to map over
 * @param {Function} callback - Callback function for each item
 * @return {boolean} - Whether every list item meets callback's truthy conditional
 */
function every(collection, callback) {
    if (typeof callback !== 'function') {
        throw new TypeError(callback + ' is not a function');
    }

    var iterator = collection.iterator();
    while (iterator.hasNext()) {
        var item = iterator.next();

        if (!callback(item)) {
            return false;
        }
    }
    return true;
}

module.exports = {
    map: map,
    forEach: forEach,
    concat: concat,
    reduce: reduce,
    pluck: pluck,
    find: find,
    first: first,
    every: every
};
