import * as ObjectUtil from './object.ts';

Object.assign(Array.prototype, {
    contains: function <T>(this: T[], element: T | ((e: T) => boolean)): boolean {
        if (typeof element === 'function') {
            for (let e of this as any) {
                if ((element as any)(e)) {
                    return true;
                }
            }
        } else {
            for (let e of this as any) {
                if (e === element) {
                    return true;
                }
            }
        }
        return false;
    },
    containsIgnoreCase: function (this: any[], element: string): boolean {
        if (typeof element === 'string') {
            for (let e of this as any) {
                if (typeof e === 'string' && (e as any).toLocaleLowerCase() === (element as any).toLocaleLowerCase()) {
                    return true;
                }
            }
        }
        return false;
    },
    remove: function <T>(this: T[], element: T | ((e: T) => boolean)): T | undefined {
        let index = -1;
        if (typeof element === 'function') {
            for (let i = 0; i < this.length; i++) {
                if ((element as any)(this[i]) === true) {
                    index = i;
                    break;
                }
            }
        } else {
            for (let i = 0; i < this.length; i++) {
                if (this[i] === element) {
                    index = i;
                    break;
                }
            }
        }
        if (index >= 0) {
            let result = this[index];
            this.splice(index, 1);
            return result;
        }
        return undefined;
    },
    removeIf: function <T>(this: T[], predicate: (e: T) => boolean): boolean {
        let changed = false;
        if (typeof predicate === 'function') {
            for (let i = this.length - 1; i >= 0; i--) {
                if ((predicate as any)(this[i]) === true) {
                    this.splice(i, 1);
                    changed = true;
                }
            }
        }
        return changed;
    },
    clone: function <T>(this: T[], deep?: boolean): T[] {
        if (deep) {
            let array = [] as any[];
            for (let e of this as any) {
                array.push(ObjectUtil.deepClone(e));
            }
            return array;
        } else {
            return this.slice(0, this.length);
        }
    },
    get: function <T>(this: T[], fn: (e: T) => boolean): T | undefined {
        for (let e of this as any) {
            if ((fn as any)(e)) {
                return e;
            }
        }
        return undefined;
    },
    equals: function <T>(this: T[], other: T[], predicate?: (a: T, b: T) => boolean): boolean {
        if (typeof predicate !== 'function') {
            predicate = (a, b) => a === b;
        }
        if (Array.isArray(other) && this.length === (other as any).length) {
            for (let thisElement of this as any) {
                for (let otherElement of other as any) {
                    if (!((predicate as any)(thisElement, otherElement))) {
                        return false;
                    }
                }
            }
            for (let otherElement of other as any) {
                for (let thisElement of this as any) {
                    if (!((predicate as any)(thisElement, otherElement))) {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    },
});

export function indexOf<T>(array: T[], element: T | ((e: T) => boolean)): number {
    if (typeof element === 'function') {
        for (let i = 0; i < array.length; i++) {
            if ((element as (e: T) => boolean)(array[i])) {
                return i;
            }
        }
        return -1;
    }
    return array.indexOf(element);
}
