/// class PD.Web.UI.Controls.GridSelectionModule
/// using PD.Web.UI.Controls.GridModule

PD.Web.UI.Controls.GridSelectionModule =
PD.Web.UI.Controls.GridModule.extend({

    //===================================
    // FIELDS
    //===================================

    _selections: null,
    _pk: null,
    _events: null,
    _lastTouchedRowPk: null,
    _lastTouchedRowUid: null,

    //===================================
    // CONSTRCUTOR
    //===================================

    init: function () {
        this._super();
        this._id = "selection";
    },

    //===================================
    // PROPERTIES
    //===================================

    get_selections: function () {
        return this._selections;
    },
    
    get_pk: function () {
        return this.getOption("pk", "ObjectId");
    },

    get_selectionMode: function () {
        return this.getOption("selectionMode", "multiple");
    },

    get_selectOnRightClick: function () {
        return this.getOption("selectOnRightClick", false);
    },

    get_defaultSelectionEnabled: function () {
        return this.getOption("defaultSelectionEnabled", true);
    },

    get_changeFn: function () {
        return this.getOption("change", undefined);
    },

    get_dblClickFn: function () {
        return this.getOption("dblclick", undefined);
    },

    get_clickFn: function () {
        return this.getOption("click", undefined);
    },

    get_lastTouchedRowUid: function () {
        return this._getLastTouchedRowUId();
    },
    
    set_lastTouchedRowUid: function (value) {
        return this._setLastTouchedRowUId(value);
    },

    //===================================
    // EVENT HANDLERS
    //===================================

    _onDataBound: function (e) {

        var selectionCount = 0;

        if (this._onDataBoundStart != null) {
            this._onDataBoundStart(e);
            this._onDataBoundStart = null;
        }

        selectionCount = this.get_selections().count();

        if (selectionCount === 0) {
            if (this.get_defaultSelectionEnabled()) {
                this._selectRowByDefault();
            }
            selectionCount = this.get_selections().count();
            if (selectionCount === 0) {
                this._fire(this.get_changeFn());
            }
            else {
                this.set_lastTouchedRowUid(_.first(this.get_selections().toArray()).uid);
            }
        }

        this._restore();

    },

    _onMouseDown: function (e) {

        var $row = $(e.target).closest("TR[data-uid]"),
            isRowAlreadySelected = $row.hasClass("k-state-selected"),
            isAllowMultiSelectEnabled = this.get_selectionMode() === "multiple",
            isShiftKeyActivated = e.shiftKey,
            isCtrlKeyActivated = (e.ctrlKey || e.metaKey),
            isLeftClick = e.button === 0,
            isRightClick = e.button === 2;

        if (isRightClick) {
            this.set_lastTouchedRowUid($row.data("uid"));
            this._handleRightClick($row, isRowAlreadySelected);
        }

        if (isLeftClick) {

            switch (true) {
                case !isCtrlKeyActivated && !isShiftKeyActivated && !isRowAlreadySelected:
                    this.set_lastTouchedRowUid($row.data("uid"));
                    this._handleSingleSelect($row, isRowAlreadySelected);
                    break;
                case isAllowMultiSelectEnabled && isCtrlKeyActivated:
                    this.set_lastTouchedRowUid($row.data("uid"));
                    this._handleMultiSelectWithCtrlKey($row, isRowAlreadySelected);
                    break;
                case isAllowMultiSelectEnabled && isShiftKeyActivated && !isRowAlreadySelected:
                    this._handleMultiSelectWithShiftKey($row, isRowAlreadySelected);
                    e.preventDefault();
                    break;
            }

        }

    },

    _onClick: function (e) {

        var $row = $(e.target).closest("TR[data-uid]"),
            isRowAlreadySelected = $row.hasClass("k-state-selected"),
            isAllowMultiSelectEnabled = this.get_selectionMode() === "multiple",
            isShiftKeyActivated = e.shiftKey,
            isCtrlKeyActivated = (e.ctrlKey || e.metaKey),
            isLeftClick = e.button === 0,
            isRightClick = e.button === 2;

        if (isLeftClick && isRowAlreadySelected) {

            switch (true) {
                case !isCtrlKeyActivated && !isShiftKeyActivated:
                    this.set_lastTouchedRowUid($row.data("uid"));
                    this._handleSingleSelect($row, isRowAlreadySelected);
                    break;
                case isAllowMultiSelectEnabled && isShiftKeyActivated:
                    this._handleMultiSelectWithShiftKey($row, isRowAlreadySelected);
                    e.preventDefault();
                    break;
            }

        }

    },

    _onDoubleClick: function (e) {
        var row = $(e.target).closest("TR[data-uid]");
        this._fire(this.get_dblClickFn());
    },

    //===================================
    // PUBLIC METHODS
    //===================================

    start: function () {
        this._super();
        this._selections = new PD.Web.UI.Controls.GridSelection(this.get_pk());
        this.addGridMethod("clear", this._clear);
    },

    getSelectedItem: function () {
        return this.getSelections().first();
    },

    getSelection: function () {
        return this.getSelections().first();
    },

    getSelections: function () {

        var result = this._selections,
            selectionMapFn = this.getOption("selection.map");

        if (_.isFunction(selectionMapFn)) {
            result = new PD.Web.UI.Controls.GridSelection(this.get_pk(), selectionMapFn({ selections: result }));
        }

        return result;

    },

    refresh: function (options) {

        var that = this;

        if (this.get_kendo() == null) {
            return;
        }

        options = options || {};

        if (options.clear === true) {
            this._clear();
        }

        if ($.isArray(options.selections)) {
            this._onDataBoundStart = function () {
                if (options.clear) {
                    that._clear();
                }
                that._addSelections(new PD.BCL.List(options.selections), options.scrollIntoView);
                this._fire(this.get_changeFn());
            };
            this.get_dataSource().read();
        }
        else {
            this.get_dataSource().read();
            this._fire(this.get_changeFn());
        }

    },

    //===================================
    // PRIVATE METHODS
    //===================================

    _handleRightClick: function ($row, isRowAlreadySelected) {

        var isSelectOnRightClickEnabled = this.get_selectOnRightClick();

        if (isSelectOnRightClickEnabled && !isRowAlreadySelected) {
            this._clear();
            $row.addClass("k-state-selected");
            this._commit();
            this._fire(this.get_changeFn(), true);
        }

    },

    _handleMultiSelectWithCtrlKey: function ($row, isRowAlreadySelected) {

        if (isRowAlreadySelected) {
            $row.removeClass("k-state-selected");
        }
        else {
            $row.addClass("k-state-selected");
            this._fire(this.get_clickFn());
        }

        this._commit();
        this._restore();

        this._fire(this.get_changeFn(), true);
        
    },

    _handleMultiSelectWithShiftKey: function ($row, isRowAlreadySelected) {
        
        var $start = this.get_kendo().content.find("TR[data-uid=" + (this.get_lastTouchedRowUid() || 0) + "]"),
            isBefore = $start.prevAll("[data-uid=" + $row.data("uid") + "]").length !== 0,
            isAfter = $start.nextAll("[data-uid=" + $row.data("uid") + "]").length !== 0;

        if ($start.length === 1) {

            this._clear();

            if (!$start.is($row)) {

                if (isBefore) {
                    $start.addClass("k-state-selected");
                    $start.prevUntil($row[0]).addClass("k-state-selected");
                }
                else {
                    $start.addClass("k-state-selected");
                    $start.nextUntil($row[0]).addClass("k-state-selected");
                }

            }

            $row.addClass("k-state-selected");

            this._commit();
            this._restore();
            this._fire(this.get_clickFn(), true);
            this._fire(this.get_changeFn(), true);

        }
        else {
            PD.Web.UI.MessageDialog.warn({
                title: "Invalid Selection",
                message: "You have attempted a selection that cannot be completed for one of the following reasons: <ul><li style='padding-bottom:10px;'>You have selected too many rows using SHIFT + SELECT. Reduce the number of rows you are attempting to select.</li><li style='padding-bottom:10px;'>You have applied a filter or sort, since making your first selection. Make a new selection before attempting a SHIFT + SELECT.</li></ul>"
            });
        }

    },

    _handleSingleSelect: function ($row, isRowAlreadySelected) {

        var selectionCount = this.getSelections().count();

        this._clear();
        $row.addClass("k-state-selected");

        this._commit();

        if (!isRowAlreadySelected || selectionCount > 1) {
            this._fire(this.get_clickFn(), true);
            this._fire(this.get_changeFn(), true);
        }

    },

    _addSelections: function (selections, scrollIntoView) {

        var that = this,
            kendo = this.get_grid().get_kendo(),
            ds = this.get_dataSource().get_instance();

        selections.each(function (index, selection) {

            var item = ds.get(selection),
                row = null;

            if (item) {
                row = kendo.content.find("TR[data-uid=" + item.uid + "]");
            }

            if (row) {
                row.addClass("k-state-selected");
            }

        });

        this._commit();

    },

    _scrollIntoView: function () {

        var data = this.getRowKeyDataByPK(id),
            uid = null,
            row = null;

        if (data !== null) {
            uid = data.uid;
        }

        if (uid !== null) {
            row = this.get_grid().find("TR[data-uid=" + uid + "]").first();
            this.get_grid().find(".k-grid-content").scrollTop(row.offset().top);
        }

    },

    _commit: function () {

        var that = this,
            grid = this.get_grid().get_kendo(),
            rows = this._getSelectedRows(),
            data = new PD.BCL.List(),
            clone = null;

        rows.each(function (index, row) {
            data.add(grid.dataItem(row));
        });

        data.each(function (index, item) {
            that.get_selections().add(item);
        });

        clone = this.get_selections().clone();

        clone.each(function (index, row) {
            if (!data.contains(row)) {
                that.get_selections().remove(row);
            }
        });

    },

    _restore: function () {

        var grid = this.get_grid().get_kendo(),
            ds = this.get_grid().get_dataSource().get_instance(),
            pk = this.get_pk();

        this.get_selections().each(function (index, item) {
            if (ds.get(item[pk]) !== undefined) {
                grid.content.find("tr[data-uid=" + ds.get(item[pk]).uid + "]").addClass("k-state-selected");
            }
        });

    },

    _clear: function () {
        this.get_kendo()
            .content
            .find(".k-state-selected")
            .removeClass("k-state-selected");
        this._selections.clear();
    },

    _getSelectedRows: function () {
        return this.get_kendo().content.find(".k-state-selected");
    },

    _fire: function (callback, isUserInitiatedEvent) {

        if ($.isFunction(callback)) {
            callback({
                grid: this.get_grid(),
                selection: this.getSelection(),
                selections: this.getSelections(),
                isUserInitiatedEvent: isUserInitiatedEvent == null ? false : isUserInitiatedEvent
            });
        }

    },
    
    _selectRowByDefault: function () {

        var data = this.get_dataSource().get_instance().data();
        var pk = this.get_pk();
        var $row = this.get_kendo().element.find("[role=grid] tbody tr:first");

        if ($row.length > 0) {
            this._selectRowByUid($row.data("uid"));
            this._commit();
            this._fire(this.get_changeFn());
        }
        
    },

    _selectRowByPK: function (keys) {

        var that = this,
            ds = that.get_dataSource().get_instance(),
            pks = new PD.BCL.List(keys),
            fireChange = false;

        pks.each(function (index, key) {
            var item = ds.get(key);
            if (item) {
                that._selectRowByUid(item.uid);
                fireChange = true;
            }
        });

        this._commit();

        if (fireChange) {
            this._fire(this.get_changeFn());
        }
        
    },

    _registerEventHandlers: function () {

        var selector = ".k-grid-content TR[data-uid]";

        this._unregisterEventHandlers();

        this.get_kendo().element.on(
            "click.selectionManagementModule",
            selector,
            $.proxy(this._onClick, this));

        this.get_kendo().element.on(
            "dblclick.selectionManagementModule",
            selector,
            $.proxy(this._onDoubleClick, this));

        this.get_kendo().element.on(
            "mousedown.selectionManagementModule",
            selector,
            $.proxy(this._onMouseDown, this));

        this.get_kendo().bind(
            "dataBound",
            $.proxy(this._onDataBound, this));

        this.addGridMethod("getSelectedItem", this.getSelectedItem);
        this.addGridMethod("refresh", this.refresh);

    },

    _unregisterEventHandlers: function () {

        this.get_kendo().element.off("click.selectionManagementModule");

        this.get_kendo().element.off("dblclick.selectionManagementModule");

        this.get_kendo().element.off("mousedown.selectionManagementModule");

        this.get_kendo().unbind("dataBound");

    },

    _selectRowByUid: function (uid) {

        var row = null;

        if (uid) {
            this.get_kendo()
                .content
                .find("TR[data-uid=" + uid + "]")
                .addClass("k-state-selected");
        }
        
    },

    _getLastTouchedRowUId: function () {

        var ds = this.get_grid().get_dataSource().get_instance();
        var $row = this.get_kendo().content.find("TR[data-uid=" + (this._lastTouchedRowUid || 0) + "]");
        var row = null;

        if ($row.length > 0) {
            return this._lastTouchedRowUid;
        }

        if (this._lastTouchedRowPk != null) {

            row = ds.get(this._lastTouchedRowPk);

            if (row != null) {
                return row.uid;
            }

        }

        return this._lastTouchedRowUid;

    },
    
    _setLastTouchedRowUId: function (value) {

        var ds = this.get_grid().get_dataSource().get_instance();
        var row = ds.getByUid(value);

        if (row != null) {
            this._lastTouchedRowPk = row[this.get_pk()];
        }

    }

});