(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.findReplace = factory());
}(this, function () { 'use strict';

  /**
   * Takes any input and guarantees an array back.
   *
   * - converts array-like objects (e.g. `arguments`) to a real array
   * - converts `undefined` to an empty array
   * - converts any another other, singular value (including `null`) into an array containing that value
   * - ignores input which is already an array
   *
   * @module array-back
   * @example
   * > const arrayify = require('array-back')
   *
   * > arrayify(undefined)
   * []
   *
   * > arrayify(null)
   * [ null ]
   *
   * > arrayify(0)
   * [ 0 ]
   *
   * > arrayify([ 1, 2 ])
   * [ 1, 2 ]
   *
   * > function f(){ return arrayify(arguments); }
   * > f(1,2,3)
   * [ 1, 2, 3 ]
   */

  function isObject (input) {
    return typeof input === 'object' && input !== null
  }

  function isArrayLike (input) {
    return isObject(input) && typeof input.length === 'number'
  }

  /**
   * @param {*} - the input value to convert to an array
   * @returns {Array}
   * @alias module:array-back
   */
  function arrayify (input) {
    if (Array.isArray(input)) {
      return input
    } else {
      if (input === undefined) {
        return []
      } else if (isArrayLike(input)) {
        return Array.prototype.slice.call(input)
      } else {
        return [ input ]
      }
    }
  }

  /**
   * Find and either replace or remove items in an array.
   *
   * @module find-replace
   * @example
   * > const findReplace = require('find-replace')
   * > const numbers = [ 1, 2, 3]
   *
   * > findReplace(numbers, n => n === 2, 'two')
   * [ 1, 'two', 3 ]
   *
   * > findReplace(numbers, n => n === 2, [ 'two', 'zwei' ])
   * [ 1, [ 'two', 'zwei' ], 3 ]
   *
   * > findReplace(numbers, n => n === 2, 'two', 'zwei')
   * [ 1, 'two', 'zwei', 3 ]
   *
   * > findReplace(numbers, n => n === 2) // no replacement, so remove
   * [ 1, 3 ]
   */

  /**
   * @param {array} - The input array
   * @param {testFn} - A predicate function which, if returning `true` causes the current item to be operated on.
   * @param [replaceWith] {...any} - If specified, found values will be replaced with these values, else removed.
   * @returns {array}
   * @alias module:find-replace
   */
  function findReplace (array, testFn) {
    const found = [];
    const replaceWiths = arrayify(arguments);
    replaceWiths.splice(0, 2);

    arrayify(array).forEach((value, index) => {
      let expanded = [];
      replaceWiths.forEach(replaceWith => {
        if (typeof replaceWith === 'function') {
          expanded = expanded.concat(replaceWith(value));
        } else {
          expanded.push(replaceWith);
        }
      });

      if (testFn(value)) {
        found.push({
          index: index,
          replaceWithValue: expanded
        });
      }
    });

    found.reverse().forEach(item => {
      const spliceArgs = [ item.index, 1 ].concat(item.replaceWithValue);
      array.splice.apply(array, spliceArgs);
    });

    return array
  }

  return findReplace;

}));
