import { LoadingController } from 'ionic-angular';
import { IListable } from '../interfaces/listable-interface';
import filter from 'async/filter';

export abstract class DefaultListable<T> implements IListable<T> {

    private _search: boolean;
    private _elements: Array<T>;
    private _findedElements: Array<T>;
    private _visibleElements: Array<T>;
    private _visibleRange: [number, number] = [0, 20];

    constructor(
        public loadingCtrl: LoadingController
    ) {
        this._search = false;
        this._elements = [];
        this._findedElements = [];
        this._visibleElements = [];
    }

    /**
     *
     */
    set elements(_elements: Array<T>) {
        this._elements = _elements;
        this.visibleElements = _elements.slice(this._visibleRange[0], this._visibleRange[1]);
    }

    /**
     *
     */
    get elements() {
        return this._elements;
    }

    /**
     *
     */
    set findedElements(_findedElements: Array<T>) {
        this._findedElements = _findedElements;
    }

    /**
     *
     */
    get findedElements() {
        return this._findedElements;
    }

    /**
     *
     */
    set visibleElements(_visibleElements: Array<T>) {
        this._visibleElements = _visibleElements;
    }

    /**
     *
     */
    get visibleElements() {
        return this._visibleElements;
    }

    /**
     *
     */
    hasVisibleElements(): boolean {
        return this._visibleElements.length != 0;
    }

    /**
     *
     */
    add(T: any): void {
        this.elements.push(T);

        if (this.elements.length < this._visibleRange[1]) {
            this._visibleElements.push(T);
        }    
    }

    /**
     *
     */
    remove(T: any): void {
        let index = this.elements.indexOf(T);

        if (this.isSearchMode()) {
            this.findedElements.splice(index, 1);
        } else {
            this.elements.splice(index, 1);
        }
        
        this._visibleElements.splice(index, 1);
    }

    /**
     *
     */
    get(T: any): T {
        let index = this.elements.indexOf(T);
        return this._elements[index];
    }

    /**
     *
     */
    list(): Array<T> {
        return this._elements;
    }

    /**
     *
     */
    trackByElements(index: number, element: any) {
        return element.id;
    }

    /**
     *
     */
    search(event: any): void {
        let loading = this.loadingCtrl.create({
            content: "Buscando, espere"
        });
        loading.present();

        let value: string = event.target.value;

        if (!value || value.length < 3) {
            loading.dismiss();
            return;
        }

        filter(this.elements, (item, cb) => {
            if (item.name.toLowerCase().indexOf(value.toLowerCase()) > -1) {
                return cb(null, true);
            }
            cb(null, false);
        }, (error, results) => {
            this.findedElements = results;
            this.visibleElements = this.findedElements.slice(this._visibleRange[0], this._visibleRange[1]);

            loading.dismiss();
        });
    }

    /**
     *
     */
    toggleSearch(): void {
        this._search = !this._search;
        this._visibleRange = [0, 20];

        if (!this._search) {
            this.findedElements = [];
            this.visibleElements = this.elements.slice(this._visibleRange[0], this._visibleRange[1]);
        }
    }

    /**
     *
     */
    isSearchMode(): boolean {
        return this._search;
    }

    /**
     *
     */
    seekRange(): void {
        this._visibleRange[0] = this._visibleRange[1];
        this._visibleRange[1] = this._visibleRange[1] + 20;

        if (this.isSearchMode()) {
            this._visibleElements = this._visibleElements.concat(this._findedElements.slice(this._visibleRange[0], this._visibleRange[1]));
        } else {
            this._visibleElements = this._visibleElements.concat(this._elements.slice(this._visibleRange[0], this._visibleRange[1]));
        }    
    }

    /**
     *
     */
    isToSeek(): boolean {
        return this.isSearchMode() ? this._visibleRange[1] >= this.elements.length : this._visibleRange[1] >= this.elements.length;
    }

    /**
     *
     */
    seek(event: any) {
        this.seekRange();

        if (this.isToSeek()) {
            event.enable(false);
        }

        event.complete();
    }
}
