/// class ProspectDirector.Web.UI.Queries.ConstraintsEditorDOM
/// using ProspectDirector.Web.UI.Controls.Control

ProspectDirector.Web.UI.Queries.ConstraintsEditorDOM = ProspectDirector.Web.UI.Controls.Control.extend({

    CSS_SELECTORS: {
        innerItem: "li[data-uid] span.k-in",
        conditions: ".-pd-condition"
    },

    //================================
    // CONSTRUCTOR
    //================================

    init: function (options) {
        this._options = $.extend(this._options, options);
        this.setDomNode("<div class=\"-pd-queries-constraints-editor\"></div>");
        this.render();
        this._init_container();
        this._init_tree();
    },

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

    get_tree: function () {
        return this.get_domNode().data("kendoTreeView");
    },

    get_dataSource: function () {
        return this.get_tree().dataSource;
    },

    get_editor: function () {
        return this.get_options().editor;
    },

    get_constraints: function () {
        return this.get_editor().get_constraints();
    },

    //================================
    // INITIALIZERS
    //================================

    _init_container: function () {

        var that = this,
            $editor = $(this.get_editor());

        this.get_container().css({
            borderWidth: "1px",
            borderStyle: "solid",
            borderColor: "#AAAAAA"
        });

        if ($.isFunction(this.get_options().change)) {
            $(this).bind("change", this.get_options().change);
        }

        if ($.isFunction(this.get_options().modify)) {
            $(this).bind("modify", this.get_options().modify);
        }

        $editor.bind("addCondition", $.proxy(function (e, data) {
            that._addCondition(data.condition, data.parentId);
        }, this));

        $editor.bind("addFilter", $.proxy(function (e, data) {
            that._addFilter(data.filter, data.parentId);
        }, this));

        $editor.bind("updateConstraint", $.proxy(function (e, data) {
            that._updateConstraint(data);
        }, this));

        $editor.bind("removeConstraint", $.proxy(function (e, data) {
            that._remove(data.id);
        }, this));

        $editor.bind("clear", $.proxy(function (e, data) {
            that._clear();
        }, this));

    },

    _init_tree: function () {

        var data = [];

        this.get_domNode().height("100%");

        if (this.get_constraints().hasRoot()) {
            data = [this.get_constraints().package()];
        }

        this.get_domNode().kendoTreeView({
            dataSource: {
                data: data
            },
            checkboxes: {
                checkChildren: true
            },
            template: $.proxy(this._tree_onTemplate, this),
            dataTextField: "columnName",
            dragAndDrop: true,
            drag: $.proxy(this._tree_onDrag, this),
            drop: $.proxy(this._tree_onDrop, this),
            dataBound: $.proxy(this._tree_onDataBound, this),
            check: $.proxy(this._tree_onCheck, this),
            collapse: function (e) {
                e.preventDefault();
            },
            change: $.proxy(this._tree_onChange, this)
        });

        this.get_tree().templates.dragClue = function (data) {
            return "<div class='k-header k-drag-clue' style=\"font-size:12px;\" ><span class='k-icon k-drag-status' />" + (data.item.columnName ? "Filter" : "Group") + "</div>";
        }

        this.get_domNode().on("dblclick",
            this.CSS_SELECTORS.innerItem,
            $.proxy(this._tree_onDblClick, this));

        this.get_domNode().on("click",
            this.CSS_SELECTORS.conditions,
            $.proxy(this._condition_onClick, this));

        this.get_domNode().on("dblclick",
            this.CSS_SELECTORS.conditions,
            $.proxy(this._condition_onClick, this));

        this.get_domNode().addClass("-pd-treeview");

        this.restoreCheckState();

        this._emptyMessage();

        this._renderConditions();

    },

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

    _tree_onTemplate: function (e) {

        if (e.item.role === "filter") {
            return "<span>" + ProspectDirector.Web.UI.Queries.FilterOperator.toMarkup(e.item) + "</span>";
        }
        else {
            return "<span class=\"-pd-constraint-label\">Group</span>";
        }

    },

    _tree_onChange: function (e) {

        var selection = this.getSelection();

        $(this).trigger("change", {
            selection: selection
        });

    },

    _tree_onDblClick: function (e) {

        var id = $(e.target).closest(".k-item").data("id"),
            data = this.get_constraints().findById(id);

        if (!data) {
            return;
        }

        if (data.get_role() === "filter") {
            $(this).trigger("editFilter", data);
        }

    },

    _tree_onDataBound: function (e) {

        var data = e.node && e.node.length > 0 ? e.sender.dataItem(e.node).children.data() : e.sender.dataSource.data(),
            list = new ProspectDirector.Web.List(data),
            index = 0;

        for (index = 0; index < data.length; index++) {
            this._nodeTemplate(data[index]);
        }

        this._emptyMessage();

    },

    _tree_onDrag: function (e) {

        var dstNode = e.dropTarget;

        if (this._isInaccurateDrop(dstNode)) {
            e.setStatusClass("k-denied");
        }

    },

    _tree_onDrop: function (e) {

        var srcNode = e.sourceNode,
            dstNode = e.destinationNode;

        if (!this._isValidDrop(e)) {
            e.setValid(false);
            return;
        }
        else {
            e.preventDefault();
        }

        switch (true) {
            case this._isCondition(srcNode):
                this._dropCondition(e);
                break;
            case this._isFilter(srcNode):
                this._dropFilter(e);
                break;
        }

    },

    _tree_onCheck: function (e) {

        var $node = $(e.node),
            data = this.get_dataSource().getByUid($node.data("uid")),
            constraint = null;

        if (data) {
            constraint = this.get_constraints().findById(data.id);
            constraint.update({
                isEnabled: data.checked
            });
        }

        this._setChildCheckState($node, data);

        this._renderConditions();

        $(this).trigger("modify");

    },

    _condition_onClick: function (e) {

        var $condition = $(e.target),
            condition = null;

        if ($condition.hasClass("-pd-condition-toggle")) {
            condition = this.get_constraints().findById($condition.data("condition-id"));
            this._toggleCondition(condition);
            $condition.html(condition.get_operator());
            $(this).trigger("modify");
        }

        e.preventDefault();
        e.stopPropagation();

    },

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

    _toggleCondition: function (condition) {

        if (condition.get_operator() === "AND") {
            condition.data("operatorName", "OR");
        }
        else {
            condition.data("operatorName", "AND");
        }

        this._renderConditions();

    },

    _updateConstraint: function (data) {

        var item = this.get_dataSource().get(data.id);

        if (item) {

            for (key in data) {
                item.set(key, data[key]);
            }

            this._updateFilterNode(item);

        }

        $(this).trigger("modify");

        this._renderConditions();

    },

    _clear: function () {

        this.get_dataSource().data([]);

        $(this).trigger("change", {
            selection: this.getSelection()
        });

        $(this).trigger("modify");

        this._renderConditions();

    },

    select: function (id) {

        var data = this.get_dataSource().get(id),
            node = this.get_tree().findByUid(data.uid);

        this.get_tree().select(node);

    },

    getSelection: function () {

        var result = null,
            selectedNode = this.get_tree().select();

        if (selectedNode.length > 0) {
            result = selectedNode.data("id");
        }

        return this.get_constraints().findById(result);

    },

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

    _triggerStateModified: function () {
        $(this).trigger("modify");
        this._renderConditions();
    },

    _addCondition: function (condition, parentId) {

        var parentData = this.get_dataSource().get(parentId),
            parentNode = null,
            conditionData = null;

        if (parentData !== undefined) {
            parentNode = this.get_tree().findByUid(parentData.uid);
        }

        conditionData = condition.toJSON();
        conditionData.checked = conditionData.isEnabled;

        this.get_tree().append(conditionData, parentNode);

        this._findAndSelectNode(condition.get_id());

        this._triggerStateModified();

    },

    _addFilter: function (filter) {

        var parent = filter.get_parent(),
            parentData = null,
            parentNode = null,
            siblingNode = null,
            filterData = null;

        this._addRootConditionIfMissing();

        if (parent.get_role() !== "condition") {
            parent = parent.get_parent();
        }

        if (parent !== null) {
            parentData = this.get_dataSource().get(parent.get_id());
        }

        if (parentData !== undefined) {
            parentNode = this.get_tree().findByUid(parentData.uid);
        }

        siblingNode = this._findFilterNodeInsertSibling(parentNode);

        filterData = filter.toJSON();
        filterData.checked = filterData.isEnabled;

        if (siblingNode) {
            this.get_tree().insertBefore(filterData, siblingNode);
        }
        else {
            this.get_tree().append(filterData, parentNode);
        }

        this._findAndSelectNode(filter.get_id());

        this._triggerStateModified();

    },

    _findFilterNodeInsertSibling: function (parentNode) {

        var result = null;

        parentNode.find(".k-item").each(function (index, node) {
            var $node = $(node);
            if ($node.data("role") === "condition") {
                result = $node;
                return false;
            }
        });

        return result && result.length > 0 ? result : null;

    },

    _setChildCheckState: function ($node, nodeData) {

        var that = this;

        $node.find(".k-item").each(function (index, item) {

            var data = that.get_tree().dataItem(item),
                constraint = that.get_constraints().findById(data.id);

            data.set("checked", nodeData.checked);
            constraint.data("isEnabled", nodeData.checked);

        });

    },

    _remove: function (id) {

        var data = this.get_dataSource().get(id),
            node = null,
            fallback = null;

        if (data !== undefined) {
            node = this.get_tree().findByUid(data.uid);
        }
        else {
            node = this.get_tree().select();
        }

        if (node.length > 0) {

            this._selectFallback(node);
            this.get_tree().remove(node);

            $(this).trigger("change", {
                selection: this.getSelection()
            });

            $(this).trigger("modify");

            this._renderConditions();

        }

    },

    _addRootConditionIfMissing: function () {
        if (this.get_dataSource().data().length === 0) {
            this._addConditionNode(this.get_constraints().get_root());
        }
    },

    _isInaccurateDrop: function (dstNode) {
        return !$(dstNode).closest(".k-in").hasClass("k-state-hover");
    },

    _isConditionlessOrFilterlessDrop: function (dstNode) {
        return !this._isConditionOrFilter(dstNode);
    },

    _isValidDrop: function (e) {

        var srcNode = $(e.sourceNode),
            dstNode = $(e.destinationNode);

        return e.dropPosition === "over"
                && !this._isConditionlessOrFilterlessDrop(dstNode)
                && e.valid;

    },

    _isConditionOrFilter: function (node) {

        node = $(node).closest(".k-item");

        return this._isCondition(node) || this._isFilter(node);

    },

    _isCondition: function (node) {
        return $(node).data("role") === "condition";
    },

    _isFilter: function (node) {
        return $(node).data("role") === "filter";
    },

    _emptyMessage: function () {

        var $messageTable = this.get_container().children("TABLE");

        if (this.get_dataSource().data().length === 0) {
            $messageTable.find("TD").html("No constraints have been added.");
            this.get_domNode().hide();
            $messageTable.show();
        }
        else {
            $messageTable.find("TD").html("");
            $messageTable.hide();
            this.get_domNode().show();
        }

    },

    _dropCondition: function (e) {

        var srcNode = $(e.sourceNode),
            dstNode = $(e.destinationNode);

        if (dstNode.data("role") === "filter") {
            dstNode = dstNode.closest("[data-role=condition]");
        }

        $(this).trigger("moveConstraint", {
            srcId: srcNode.data("id"),
            dstId: dstNode.data("id")
        });

        this.get_tree().append(srcNode, dstNode);

        this._triggerStateModified();

    },

    _dropFilter: function (e) {

        var srcNode = $(e.sourceNode),
            dstNode = $(e.destinationNode),
            firstConditionNode = null;

        if (dstNode.data("role") === "filter") {
            dstNode = dstNode.closest("[data-role=condition]");
        }

        firstConditionNode = dstNode.find("UL:first > [data-role=condition]").first();

        $(this).trigger("moveConstraint", {
            srcId: srcNode.data("id"),
            dstId: dstNode.data("id")
        });

        if (firstConditionNode.length === 0) {
            this.get_tree().append(srcNode, dstNode);
        }
        else {
            this.get_tree().insertBefore(srcNode, firstConditionNode);
        };

        this._triggerStateModified();

    },

    _selectFallback: function (node) {

        var fallbackNode = node.prev();

        if (fallbackNode.length === 0) {
            fallbackNode = node.next();
        }

        if (fallbackNode.length === 0) {
            fallbackNode = node.closest(".k-group").closest(".k-item");
        }

        this.get_tree().select(fallbackNode);

    },

    _findAndSelectNode: function (nodeId) {

        var result = this.get_domNode().find("[data-id=" + nodeId + "]");

        this.get_tree().select(result);

        return result;

    },

    _renderConditions: function () {

        var that = this,
            $tree = $(this.get_tree().element);

        $tree.find(this.CSS_SELECTORS.conditions).remove();

        $tree.find("LI.k-item").each(function (index, node) {
            that._setConstraint($(node));
        });

        this._setTailingCondition();

        if ($tree.find(".k-item[data-role=condition]").length <= 1) {
            $tree.find(".k-item > DIV > .k-in > .-pd-condition-internal").css({
                right: "0px",
                zIndex: 100
            });
        }

    },

    _setConstraint: function ($node) {

        var resolver = new ProspectDirector.Web.UI.Queries.ConditionResolver({
            constraints: this.get_constraints(),
            filterId: $node.data("id")
        });

        resolver = resolver.resolve();

        if (!resolver.condition) {
            return;
        }

        switch (resolver.constraint.get_role()) {
            case "condition":
                this._setGroup($node, resolver);
                break;
            case "filter":
                this._setFilter($node, resolver);
                break;
        }

    },

    _setFilter: function ($node, resolver) {

        var $inner = $node.find(".k-in:first");

        $node.children("DIV").children(".k-checkbox").css({
            backgroundColor: resolver.constraint.get_parent().data("color")
        });

        this._setFilterInnerCondition($inner, resolver.constraint, resolver.condition);
        this._setFilterOuterCondition($inner, resolver.constraint, resolver.condition);

    },

    _setGroup: function ($node, resolver) {

        var $inner = $node.find(".k-in:first");

        $node.children("DIV").children(".k-checkbox").css({
            backgroundColor: resolver.condition.data("color")
        });

        this._setGroupInnerCondition($inner, resolver.constraint, resolver.condition);
        this._setGroupOuterCondition($inner, resolver.constraint, resolver.condition);

    },

    _setFilterInnerCondition: function ($container, constraint, group) {

        var $condition = $("<div>&nbsp;</div>"),
            isLastContraint = constraint.isLast();

        $condition.attr("data-condition-id", group.get_id());
        $condition.addClass("-pd-condition");
        $condition.addClass("-pd-condition-internal");
        $condition.css({
            borderRightColor: constraint.get_parent().data("color")
        });

        if (!isLastContraint) {
            $condition.addClass("-pd-condition-toggle");
            $condition.html(group.get_operator());
        }

        $container.append($condition);

    },

    _setFilterOuterCondition: function ($container, constraint, group) {

        var $condition = $("<div>&nbsp;</div>"),
            isLastContraint = constraint.isLast();

        $condition.attr("data-condition-id", group.get_id());
        $condition.addClass("-pd-condition");
        $condition.addClass("-pd-condition-external");

        if (isLastContraint) {

            $condition.html(group.get_operator());
            $condition.addClass("-pd-condition-toggle");
            $condition.css({
                borderRightColor: group.data("color")
            });

        }

        $container.append($condition);

    },

    _setGroupInnerCondition: function ($container, constraint, group) {

        var $condition = $("<div>&nbsp;</div>"),
            siblingFilterCount = 0,
            isFirstGroup = false;

        $condition.attr("data-condition-id", group.get_id());
        $condition.addClass("-pd-condition");
        $condition.addClass("-pd-condition-internal");
        $condition.css({
            backgroundColor: "#F5F5F5",
            borderRightColor: group.data("color")
        });

        $container.append($condition);

    },

    _setGroupOuterCondition: function ($container, constraint, group) {

        var $condition = $("<div>&nbsp;</div>"),
            siblingFilterCount = 0,
            isFirstGroup = constraint.isFirst();

        $condition.html("&nbsp;");
        $condition.addClass("-pd-condition");
        $condition.addClass("-pd-condition-external");

        if (constraint.get_parent()) {

            siblingFilterCount = constraint.get_parent().get_filters().count();

            if (!isFirstGroup || (isFirstGroup && siblingFilterCount > 0)) {

                $condition.css({
                    backgroundImage: "url(" + ProspectDirector.RUNTIME.resolveUrl("~/application/icons/misc/arrow_turn_left.png") + ")",
                    backgroundRepeat: "no-repeat",
                    backgroundPosition: "center center"
                });

            }

        }

        $container.append($condition);

    },

    _setTailingCondition: function () {

        var tailingConstraint = null,
            $tailingConstraint = null,
            $tailingCondition = null,
            $tree = $(this.get_tree().element);

        $tailingConstraint = $tree.find("LI.k-item:last");

        if ($tailingConstraint.data("role") === "filter") {

            tailingConstraint = this.get_constraints().findById($tailingConstraint.data("id"));

            this._setFilter($tailingConstraint, {
                constraint: tailingConstraint,
                condition: tailingConstraint.get_parent(),
                inGroup: true
            });

            $tailingCondition = $tailingConstraint.find("DIV > .k-in .-pd-condition-external");

            $tailingCondition.removeClass("-pd-condition-toggle");
            $tailingCondition.css({
                borderRightColor: "#DDDDDD"
            });

            $tailingCondition.empty();

        }

    },

    _nodeTemplate: function (data) {

        var $node = null;

        if (data) {

            $node = this.get_tree().findByUid(data.uid);
            $node.attr("data-id", data.id);
            $node.attr("data-role", data.role);

            if (data.role === "condition") {

                $node.find(".k-in").first().closest("LI").css({
                    backgroundColor: data.color
                });

            }

        }

    },

    _updateFilterNode: function (data) {

        var $span = $("<span></span>"),
            $node = this.get_tree().findByUid(data.uid),
            $inner = $node.find(".k-in").first();

        $span.html(ProspectDirector.Web.UI.Queries.FilterOperator.toMarkup(data));

        $inner.empty();
        $inner.html($span[0].outerHTML);

    },

    restoreCheckState: function () {

        var that = this;

        $(".k-checkbox > INPUT").each(function (index, node) {

            var check = $(node),
                constraint = null,
                item = check.closest(".k-item"),
                data = that.get_tree().dataItem(item);

            constraint = that.get_constraints().findById(item.data("id"));
            data.set("checked", constraint.data("isEnabled"));

        });

    },

    render: function () {
        this._super();
        this.get_container().append("<table data-id=\"message\" style=\"height:100%;width:100%;\"><tbody><tr><td style=\"vertical-align:middle;text-align:center;font-family:arial;font-size:13px;color:#999999;\"></td></tr></tbody></table>");
    }

});
