﻿/* ************************************************************************************ */
/* LINQ to JavaScript (JSLINQ) v1.03 - http://jslinq.com                                */
/* Copyright (C) 2008 Chris Pietschmann (http://pietschsoft.com). All Rights Reserved.  */
/* This project is licensed under the Microsoft Reciprocal License (Ms-RL)              */
/* This license can be found here: http://www.codeplex.com/JSLINQ/license               */
/* ************************************************************************************ */

function From(array){
/// <summary>From LINQ to JavaScript Operator. This method was included to support it's LINQ counterpart.</summary>
/// <param name="array">The Array to return.</param>
/// <returns>Array</returns>
    return array;
};

Array.prototype.WhereAndMatch = function(clause) {
    /// <summary>Where LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine query matches.</param>
    var item;
    var newArray = [];
    var matchIndex = [];

    // The clause was passed in as a Method that return a Boolean
    var iLength = this.length;
    for (var ix = 0; ix < iLength; ix++) {
        var val = this[ix];
        if (clause(val, ix)) {
            newArray.push(val);
            matchIndex.push(ix);
        }
    }

    return { items: newArray, matches: matchIndex };
};
Array.prototype.Where = function(clause){
    return this.WhereAndMatch(clause).items;
}
Array.prototype.Top = function(number) {
    var newArray = [];
    var iLen = this.length;
    for (var i = 0; i < number; i++) {
        if (iLen <= i) break;
        newArray.push(this[i]);
    }
    return newArray;
}
Array.prototype.Select = function(clause) {
    /// <summary>Select LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine what values to select.</param>
    var item;
    var newArray = [];
    // The clause was passed in as a Method that returns a Value
    var iLen = this.length;
    for (var i = 0; i < iLen; i++) {
        var val = clause(this[i]);
        if (val) newArray.push(val);
    }
    return newArray;
};

Array.prototype.OrderBy = function(clause){
    /// <summary>OrderBy LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine how to order the data.</param>
    var clauseMethod = clause;    
    var tempArray = this.clone();
    
    return tempArray.sort(function(a, b){
        var x = clauseMethod(a);
        var y = clauseMethod(b);
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
};

Array.prototype.OrderByDescending = function(clause){
    /// <summary>OrderByDescending LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine how to order the data.</param>
    var clauseMethod = clause;
    var tempArray = this.clone();
    
    return tempArray.sort(function(a, b){
        var x = clauseMethod(b);
        var y = clauseMethod(a);
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
};

Array.prototype.SelectMany = function(clause) {
    /// <summary>SelectMany LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine what values to select.</param>
    var clauseMethod = clause;

    var retVal = [];
    var iLen = this.length;
    for (var i = 0; i < iLen; i++) {
        retVal = retVal.concat(clauseMethod(this[i]));
    }
    return retVal;
};

Array.prototype.Count = function(clause){
    /// <summary>Count LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine what values to count.</param>
    if (clause == null) return this.length;
    return this.Where(clause).length;
};

Array.prototype.Distinct = function(clause) {
    /// <summary>Distinct LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine what values to select.</param>
    var clauseMethod = function(item) { return item; };
    if (clause != null) clauseMethod = clause;

    var item;
    var dict = new Hash();
    var retVal = [];

    for (var i = 0; i < this.length; i++) {
        var val = this[i];
        item = clauseMethod(val);
        if (dict.get(item) == null) {
            dict.set(item, true);
            retVal.push(val);
        }
    }
    dict = null;
    return retVal;
};

Array.prototype.Any = function(clause) {
    /// <summary>Any LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine if a match exists.</param>
    var clauseMethod = clause;

    var iLen = this.length;
    for (var index = 0; index < iLen; index++) {
        if (clauseMethod(this[index], index)) { return true; }
    }

    return false;
};

Array.prototype.All = function(clause) {
    /// <summary>All LINQ to JavaScript Operator</summary>
    /// <param name="clause">The clause used to determine if a match exists.</param>
    var clauseMethod = clause;

    var iLen = this.length;
    for (var index = 0; index < iLen; index++) {
        if (!clauseMethod(this[index], index)) { return false; }
    }

    return true;
};

Array.prototype.Reverse = function(){
    /// <summary>Reverse LINQ to JavaScript Operator</summary>
    return this.reverse();
};

Array.prototype.First = function(clause) {
    /// <summary>First LINQ to JavaScript Operator</summary>
    if (clause != null) {
        var clauseMethod = clause;
        var iLen = this.length;
        for (var index = 0; index < iLen; index++) {
            var val = this[index];
            if (clauseMethod(val, index)) return val;
        }
        return null;
    }

    // If no clause was specified, then return the First element in the Array
    if (this.length > 0)
        return this[0];
    else
        return null;
};

Array.prototype.Last = function(clause) {
    /// <summary>Last LINQ to JavaScript Operator</summary>
    var iLen = this.length;
    if (clause != null) {
        var clauseMethod = clause;
        for (var index = iLen - 1; index >= 0; index--) {
            var val = this[index];
            if (clauseMethod(val, index)) return val;
        }
        return null;
    }
    else {
        // If no clause was specified, then return the First element in the Array
        if (iLen > 0)
            return this[iLen - 1];
        else
            return null;
    }
};

Array.prototype.ElementAt = function(index){
    /// <summary>ElementAt LINQ to JavaScript Operator</summary>
    return this[index];
};

Array.prototype.Concat = function(array){
    /// <summary>Concat LINQ to JavaScript Operator - Is actually Idendical to the Array.concat method.</summary>
    return this.concat(array);
};


Array.prototype.Intersect = function(secondArray, clause){
    /// <summary>Intersect LINQ to JavaScript Operator</summary>
    var clauseMethod;
    if (clause != undefined)
    {
        if (typeof(clause) == "string")
        {
            clauseMethod = function(item, index, item2, index2){return eval(clause);};
        }
        else
        {
            clauseMethod = clause;
        }
    }
    else
    {
        clauseMethod = function(item, index, item2, index2){return item == item2;};
    }
    
    var result = [];
    for(var a = 0; a < this.length; a++){
        for(var b = 0; b < secondArray.length; b++){
            if (clauseMethod(this[a], a, secondArray[b], b)){
                result[result.length] = this[a];
            }
        }
    }
    return result;
};

Array.prototype.DefaultIfEmpty = function(defaultValue){
    var result = this;
    if (this.length == 0){
        result = defaultValue;
    }
    return result;
};

Array.prototype.ElementAtOrDefault = function(index, defaultValue){
    if(index >= 0 && index < this.length){
        return this[index];
    }
    return defaultValue;
};

Array.prototype.FirstOrDefault = function(defaultValue){
    return this.ElementAtOrDefault(0, defaultValue);
};

Array.prototype.LastOrDefault = function(defaultValue){
    return this.ElementAtOrDefault(this.length - 1, defaultValue);
};