var List = {

	isEmpty: function(list) {
		return Variable.isEmpty(list, 'list');
	},

	walk: function(list, func) {
		if (typeof(list.forEach) === 'function') {
			return list.forEach(func);
		}
		else {
			return List.iterate(list, 'walk', func);
		}
	},

	delayedWalk: function(list, delay, func, onEnd) {
		var l = list.length;
		var i = 0;
		(function runFunc() {
			if (i < l) {
				func(list[i]);
				i++;
				setTimeout(runFunc, delay);
			}
			else {
				if (typeof(onEnd) === 'function') {
					onEnd();
				}
			}
		})();
	},

  has: function(list, func_or_val) {
    var func = typeof(func_or_val) === 'function' ? func_or_val : function(_val) { return func_or_val === _val; };
    return List.iterate(list, 'has', func);
  },

	inArray: function(needle, haystack) {
		return List.select(haystack, function(item) { return needle === item; }).length > 0;
	},

	breakIntoPages: function(items, itemsPerPage) {
		var pages = new Array(Math.floor(items.length / itemsPerPage));
		List.walk(items, function(item, index) {
			var pageNumber = Math.floor(index / itemsPerPage);
			if (index % itemsPerPage === 0) {
				pages[pageNumber] = [];
			}
			pages[pageNumber].push(item);
		});
		return pages;
	},

  filter: function(list, func) {
    return List.select(list, func);
  },

	select: function(list, func) {
		if (typeof(list.filter) === 'function') {
			return list.filter(func);
		}
		else {
			return List.iterate(list, 'select', func);
		}
	},

  reject: function(list, func) {
    var newFunc = function(item, index) { return func(item, index) === false; };
    return List.select(list, newFunc);
  },

	map: function(list, func) {
		if (typeof(list.map) === 'function') {
			return list.map(func);
		}
		else {
			return List.iterate(list, 'map', func);
		}
	},

  onCompleteIterationResult: function(result, method, item, newList) {
    switch(method) {
    case 'select':
      if (result === true) {
        newList.push(item);
      }
      break;
    case 'map':
      newList.push(result);
      break;
    }
  },

  onLazyIterationResult: function(result, method) {
    switch(method) {
    case 'detect':
      if (result === true) {
        return item;
      }
    case 'has':
      if (result === true) {
        return true;
      }
    }
  },

	iterate: function(list, method, callback) {
		if (typeof(callback) !== 'function') {
			throw new Error("Iterate: callback is not a valid function.");
		}
		var newList = [];
		var result;
    var lazyResult;
    if (list.length !== undefined && list.length > 0) {
      var l = list.length;
      if (list[0] !== undefined) {
        for (var i = 0; i < list.length; i++) {
          result = callback(list[i], i);
          if (method === 'select' || method === 'map') {
            List.onCompleteIterationResult(result, method, list[i], newList);
          }
          else if (method === 'detect' || method === 'has') {
            if ((lazyResult = List.onLazyIterationResult(result, method)) !== undefined) {
              return lazyResult;
            }
          }
        }
      }
      else {
        for (var i in list) {
          result = callback(list[i], i);
          if (method === 'select' || method === 'map') {
            onCompleteIterationResult(result, method, list[i], newList);
          }
          else if (method === 'detect' || method === 'has') {
            if ((lazyResult = onLazyIterationResult(result, method)) !== null) {
              return lazyResult;
            }
          }
        }
      }
    }
    if (method === 'select' || method === 'map') {
      return newList;
    }
    else if (method === 'has') {
      return false;
    }
    else if (method === 'detect') {
      return null;
    }
	},

  cloneList: function(list) {
    var clone = [];
    if (list.length !== undefined && list.length > 0) {
      if (list[0] !== undefined) {
        for (var i = 0; i < list.length; i++) {
          clone[i] = list[i];
        }
      }
      else {
        for (var i in list) {
          clone[i] = list[i];
        }
      }
    }
    return clone;
  },

  Factory: function(array) {
    var list = List.cloneList(array);
          
    this.each = function(func) {
      List.walk(array, func);
      return this;
    };

    this.select = function(func) {
      return new List.Factory(List.select(list, func));
    };

    this.has = function(func) {
      return List.has(list, func);
    }

    this.reject = function(func) {
      return new List.Factory(List.reject(list, func));
    };

    this.length = list.length;

    this.clone

    return this;
  }

};

