import { BaseView } from "./base-view";
import { PouchService } from "../services/pouch-service";
import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";

import { EventsManager } from "../base/events/event-manager";

export abstract class BaseListView<T> extends BaseView<T>{

    items: T[];
    selectedIndex: number;
    filters: Array<[string, string, any]>;

    constructor(c: { new (): T; }, ...filters: Array<[string, string, any]>) {
        super(c, PouchService);

        this.items = [];
        this.selectedIndex = -1;
        this.filters = filters;

        this.initialize();
    }

    /**
     * 
     */
    initialize() {
        EventsManager.publish("app:loading", true);

        super.getInjectable(PouchService).getAll(this.getModelName()).subscribe(result => {
            this.applyFilter(result.docs);            
            this.subcribeToEvents();

            EventsManager.publish("app:loading", false);
        }, error => { 
            console.log(error);

            EventsManager.publish("app:loading", false);
            EventsManager.publish("app:error", error);
        });
    }

    /**
     * 
     */
    private subcribeToEvents(): void {
        EventsManager.subscribe("app:changed", data => {
            if (data.action === "add") {
                this.items.unshift(data.item);
            }

            if (data.action === "update") {
                this.items[this.getSelectedIndex()] = data.item;
            }

            if (data.action === "delete") {
                this.items.splice(this.getSelectedIndex(), 1);
            }

            this.setSelectedIndex(-1);
        });
    }

    /**
     * 
     */
    applyFilter(records: T[]): void {
        if (records.length == 0) {
            return;
        }
        
        if (this.getFilters().length == 0) {
            this.items = records;

            return;
        }

        this.items = records.filter(record => {
            let expressions = this.getFilters().map(filter => {
                let operator = "===";
                let leftOperand = "record." + filter[0];
                let rightOperand = typeof(filter[2]) === "string" ? "'" + filter[2] + "'" : filter[2];

                if (filter[1] === "!=") {
                    operator = "!==";
                }

                if (filter[1] === ">") {
                    operator = ">";
                }

                if (filter[1] === "<") {
                    operator = "<";
                }

                if (filter[1] === ">=") {
                    operator = "<=";
                }

                if (filter[1] === "<=") {
                    operator = "<=";
                }

                return leftOperand + operator + rightOperand;
            });

            return eval(expressions.join("&&"));
        });
    }
    
    /**
     * 
     */
    performDelete(): void {
        super.getInjectable(PouchService).remove(this.getSelectedItem()).subscribe(result => { 
            EventsManager.publish("app:changed", {
                action: "delete",
                details: result
            });
        }, error => { 
            EventsManager.publish("app:error", error);
        });
    }

    /**
     * 
     */
    getItems(): T[] {
        return this.items;
    }
    
    /**
     * 
     * @param items 
     */
    setItems(items: T[]): void {
        this.items = items;

        this.applyFilter(this.getItems());
    }

    /**
     * 
     */
    getSelectedIndex(): number {
        return this.selectedIndex;
    }

    /**
     * 
     * @param index 
     */
    setSelectedIndex(selectedIndex: number): void {
        this.selectedIndex = selectedIndex;
    }

    /**
     * 
     * @param item 
     */
    indexOf(item: T): number {
        return this.getItems().indexOf(item);
    }

    /**
     * 
     */
    getSelectedItem(): T {
        if (this.getSelectedIndex() === -1) {
            return null;
        }

        return this.getItems()[this.getSelectedIndex()];
    }

    /**
     * 
     * @param item 
     */
    setSelectedItem(item: T): void {
        let index = this.indexOf(item);
        this.setSelectedIndex(index);
    }

    /**
     * 
     */
    deselectItem(): void {
        this.setSelectedIndex(-1);
    }

    /**
     * 
     */
    getFilters(): Array<[string, string, any]> {
        return this.filters;
    }

    /**
     * 
     * @param filters 
     */
    setFilters(filters: Array<[string, string, any]>): void {
        this.filters = filters;

        this.applyFilter(this.getItems());
    }
} 