/**
 * Developer: Stepan Burguchev
 * Date: 8/19/2014
 * Copyright: 2009-2016 Comindware®
 *       All Rights Reserved
 * Published under the MIT license
 */

"use strict";

import 'lib';

/*
* This is a modified version of Backbone.Picky with extended list of features related to multiselect collections
*
* */

let SelectableBehavior = {};

// SelectableBehavior.SingleSelect
// ------------------
// A single-select mixin for Backbone.Collection, allowing a single
// model to be selected within a collection. Selection of another
// model within the collection causes the previous model to be
// deselected.

SelectableBehavior.SingleSelect = function(collection){
    this.collection = collection;
};

_.extend(SelectableBehavior.SingleSelect.prototype, {

    // Select a model, deselecting any previously
    // selected model
    select: function(model){
        if (model && this.selected === model) { return; }

        this.deselect();

        this.selected = model;
        this.selected.select();
        this.lastSelectedModel = model.cid;
        this.cursorCid = model.cid;
        this.trigger("select:one", model);
    },

    // Deselect a model, resulting in no model
    // being selected
    deselect: function(model){
        if (!this.selected){ return; }

        model = model || this.selected;
        if (this.selected !== model){ return; }

        this.lastSelectedModel = undefined;
        this.cursorCid = undefined;
        this.selected.deselect();
        if (this.selected !== undefined) {
            this.trigger("deselect:one", this.selected);
            delete this.selected;
        }
    }

});

// SelectableBehavior.MultiSelect
// -----------------
// A mult-select mixin for Backbone.Collection, allowing a collection to
// have multiple items selected, including `selectAll` and `selectNone`
// capabilities.

SelectableBehavior.MultiSelect = function (collection) {
    this.collection = collection;
    this.selected = {};
};

_.extend(SelectableBehavior.MultiSelect.prototype, {

    // Select a specified model, make sure the
    // model knows it's selected, and hold on to
    // the selected model.
    select: function (model) {
        if (this.selected[model.cid]) { return; }

        this.selected[model.cid] = model;
        model.select();
        calculateSelectedLength(this);
    },

    // Select a specified model and update selection for the whole collection according to the key modifiers
    selectSmart: function (model, ctrlPressed, shiftPressed)
    {
        var collection = this;
        if (!ctrlPressed && !shiftPressed) {
            // with no hotkeys we select this item and deselect the others
            collection.selectNone();
            model.select();
            collection.lastSelectedModel = model.cid;
            collection.cursorCid = model.cid;
        } else if (shiftPressed) {
            // if shift or ctrl+shift is pressed we select the items in range [lastSelectedItem, thisItem] and deselect the others
            var lastSelectedModel = collection.lastSelectedModel;
            if (!lastSelectedModel) {
                // we select this item alone if this is the first click
                collection.selectNone();
                model.select();
                collection.cursorCid = model.cid;
            } else {
                // if not, we select the range
                var lastSelectedIndex = 0;
                var thisIndex = 0;
                collection.each(function (m, i) {
                    if (m.cid === lastSelectedModel) {
                        lastSelectedIndex = i;
                    }
                    if (m === model) {
                        thisIndex = i;
                    }
                });
                var startIndex = Math.min(lastSelectedIndex, thisIndex);
                var endIndex = Math.max(lastSelectedIndex, thisIndex);
                var models = collection.models;
                collection.selectNone();
                for (var i = startIndex; i <= endIndex; i++) {
                    models[i].select();
                }
                collection.cursorCid = models[thisIndex].cid;
            }
        } else if (ctrlPressed) {
            // adding this item to the multiple selection list
            model.select();
            collection.lastSelectedModel = model.cid;
        }
    },

    // Deselect a specified model, make sure the
    // model knows it has been deselected, and remove
    // the model from the selected list.
    deselect: function (model) {
        if (!this.selected[model.cid]) { return; }

        delete this.selected[model.cid];
        model.deselect();
        calculateSelectedLength(this);
    },

    // Select all models in this collection
    selectAll: function () {
        this.each(function (model) { model.select(); });
        calculateSelectedLength(this);
    },

    // Deselect all models in this collection
    selectNone: function () {
        this.each(function (model) { model.deselect(); });
        calculateSelectedLength(this);
    },

    // Toggle select all / none. If some are selected, it
    // will select all. If all are selected, it will select
    // none. If none are selected, it will select all.
    toggleSelectAll: function () {
        if (this.selectedLength === this.length) {
            this.selectNone();
        } else {
            this.selectAll();
        }
    }
});

// SelectableBehavior.Selectable
// ----------------
// A selectable mixin for Backbone.Model, allowing a model to be selected,
// enabling it to work with SelectableBehavior.MultiSelect or on it's own

SelectableBehavior.Selectable = function (model) {
    this.model = model;
};

_.extend(SelectableBehavior.Selectable.prototype, {

    // Select this model, and tell our
    // collection that we're selected
    select: function () {
        if (this.selected) { return; }

        this.selected = true;
        this.trigger("selected", this);

        if (this.collection) {
            this.collection.select(this);
        }
    },

    // Deselect this model, and tell our
    // collection that we're deselected
    deselect: function () {
        if (!this.selected) { return; }

        this.selected = false;
        this.trigger("deselected", this);

        if (this.collection) {
            this.collection.deselect(this);
        }
    },

    // Change selected to the opposite of what
    // it currently is
    toggleSelected: function () {
        if (this.selected) {
            this.deselect();
        } else {
            this.select();
        }
    }
});

// Helper Methods
// --------------

// Calculate the number of selected items in a collection
// and update the collection with that length. Trigger events
// from the collection based on the number of selected items.
let calculateSelectedLength = function (collection) {

    collection.selectedLength = _.filter(collection.models, model  => model.selected).length;

    var selectedLength = collection.selectedLength;
    var length = collection.length;

    if (selectedLength === length) {
        collection.trigger("select:all", collection);
        return;
    }

    if (selectedLength === 0) {
        collection.trigger("select:none", collection);
        return;
    }

    if (selectedLength > 0 && selectedLength < length) {
        collection.trigger("select:some", collection);
    }
};

export default SelectableBehavior;
export var Selectable = SelectableBehavior.Selectable;
export var SingleSelect = SelectableBehavior.SingleSelect;
export var MultiSelect = SelectableBehavior.MultiSelect;
