import { map, invert } from 'underscore';
import { eachRight } from './underscore-compat';
import { View, Model } from 'backbone';

export default class CollectionView<SubView extends View = View> extends View {
    items: SubView[];
    container: string;
    _container: JQuery<Element>;
    subview: { new (options?: any): SubView };

    initCollectionEvents(): this {
        return this.listenTo(this.collection, {
            add: this.insertItem,
            remove: this.removeItem,
            sort: this.sortItems,
            update: this.placeItems,
            reset: this.resetItems,
        });
    }

    render(): this {
        this.beforeRender();
        this.detachItems();
        this.renderContainer();
        this._container = (this.container ? this.$(this.container).first() : this.$el);
        this.placeItems();
        this.afterRender();
        return this;
    }

    beforeRender() { return this; }
    renderContainer() { return this; }
    afterRender() { return this; }

    remove(): this {
        this.clearItems();
        super.remove();
        return this;
    }

    makeItem(model?) {
        return new this.subview({model});
    }

    initItems(): this {
        this.items = this.collection.map(this.makeItem.bind(this));
        return this;
    }

    clearItems(): this {
        eachRight(this.items, item => item.remove());
        return this;
    }

    insertItem(model, collection?, options?: any): this {
        let item = this.makeItem(model);
        let index = options && options.index;
        if (index != undefined) {
            this.items.splice(index, 0, item);
        } else {
            this.items.push(item);
        }
        return this;
    }

    removeItem(model, collection, options: any): this {
        let index = options.index;
        this.items[index].remove();
        this.items.splice(index, 1);
        return this;
    }

    sortItems(): this {
        let order = invert(map(this.collection.models, 'cid'));
        this.items.sort((l, r) => +order[l.model.cid] - +order[r.model.cid]);
        return this;
    }

    detachItems(): this {
        eachRight(this.items, item => item.$el.detach());
        return this;
    }

    placeItems(): this {
        // No need to detach the items first.
        // Each item is unique, so jQuery moves rather than copies it.
        let container = this._container;
        container.append(map(this.items, 'el'));
        return this;
    }

    resetItems(): this {
        return this.clearItems().initItems().placeItems();
    }
}
