/**
 * Vitche jQuery Tree Control
 * @author Maxim Kazakov <kazakov.max@gmail.com>
 **/
var CLASS_JQUERY_TREE                   = 'jquery-tree';
var CLASS_JQUERY_TREE_CONTROLS          = 'jquery-tree-controls';
var CLASS_JQUERY_TREE_COLLAPSE_ALL      = 'jquery-tree-collapseall';
var CLASS_JQUERY_TREE_EXPAND_ALL        = 'jquery-tree-expandall';
var CLASS_JQUERY_TREE_COLLAPSED         = 'jquery-tree-collapsed';
var CLASS_JQUERY_TREE_HANDLE            = 'jquery-tree-handle';
var CLASS_JQUERY_TREE_HANDLE_COLLAPSE   = 'jquery-tree-handle-collapse';
var CLASS_JQUERY_TREE_TITLE             = 'jquery-tree-title';
var CLASS_JQUERY_TREE_NODE              = 'jquery-tree-node';
var CLASS_JQUERY_TREE_LEAF              = 'jquery-tree-leaf';
var CLASS_JQUERY_TREE_DELETE_LINK       = 'jquery-tree-delete-link';
var CLASS_JQUERY_TREE_EDIT_LINK         = 'jquery-tree-edit-link';
var CLASS_JQUERY_TREE_ADDITIONAL_LINKS  = 'jquery-tree-additional-links';

(function($) {

    var COLLAPSE_ALL_CODE   = '<span class="' + CLASS_JQUERY_TREE_COLLAPSE_ALL  + '">+&nbsp;Свернуть все</span>';
    var EXPAND_ALL_CODE     = '<span class="' + CLASS_JQUERY_TREE_EXPAND_ALL    + '">&minus;&nbsp;Развернуть все</span>';
    var TREE_CONTROLS_CODE  = '<div  class="' + CLASS_JQUERY_TREE_CONTROLS      + '">' +
    COLLAPSE_ALL_CODE +
    '&nbsp;' +
    EXPAND_ALL_CODE +
    '</div>';

    var TREE_NODE_HANDLE_CODE           = '<span class="' + CLASS_JQUERY_TREE_HANDLE + '">+</span>';
    var TREE_NODE_HANDLE_COLLAPSED      = "+";
    var TREE_NODE_HANDLE_EXPANDED       = "&minus;";

    $.fn.extend({
        /**
         * Делает дерево из структуры вида:
         * <ul>
         *   <li><label>Item1</label></li>
         *   <li>
         *     <label>ItemWithSubitems</label>
         *     <ul>
         *       <li><label>Subitem1</label></li>
         *     </ul>
         *   </li>
         * </ul>
         */
        Tree: function(options) {
            var tree = this;

            options = $.extend({
                expandAtStart           : true,
                dragNdrop               : false,
                onDrop                  : null,
                sortable                : false,
                onSortUpdate            : null,
                isRootDeletable         : true,
                deletable               : false,
                deletableSelector       : null,
                onBeforeDelete          : null,
                onAfterDelete           : null,
                deleteLink              : null,
                isRootEditable          : true,
                editable                : false,
                editableSelector        : null,
                onBeforeEdit            : null,
                onEdit                  : null,
                editLink                : null,
                additionalLinks         : null
            }, options);

            function init() {
                // Добавление контролов для всего дерева
                // (все свернуть, все развернуть), добавление класса
                $(tree)
                    .addClass(CLASS_JQUERY_TREE)
                    .before(TREE_CONTROLS_CODE)
                    .prev('.' + CLASS_JQUERY_TREE_CONTROLS)
                .find('.' + CLASS_JQUERY_TREE_COLLAPSE_ALL)
                    .click(collapseAll)
                .parent('.' + CLASS_JQUERY_TREE_CONTROLS).find('.' + CLASS_JQUERY_TREE_EXPAND_ALL)
                    .click(expandAll);

                $('li', tree).find(':first').addClass(CLASS_JQUERY_TREE_TITLE)
                                .closest('li').addClass(CLASS_JQUERY_TREE_LEAF);

                // Для всех элементов, являющихся узлами (имеющих дочерние элементы)...
                $('li:has(ul:has(li))', tree).find(':first')
                    // ... добавим элемент, открывающий/закрывающий узел
                    .before(TREE_NODE_HANDLE_CODE)
                    // ... добавим к контейнеру класс "узел дерева" и "свернем".
                    .closest('li')
                        .addClass(CLASS_JQUERY_TREE_NODE)
                        .addClass(CLASS_JQUERY_TREE_COLLAPSED)
                        .removeClass(CLASS_JQUERY_TREE_LEAF);

                // ... повесим обработчик клика
                $('.' + CLASS_JQUERY_TREE_HANDLE, tree).live("click.changeState", changeCollapsedState);

                // Раскрытие всего дерева при необходимости
                if (options.expandAtStart) {
                    $(tree)
                        .find("li:has(ul)")
                            .removeClass(CLASS_JQUERY_TREE_COLLAPSED)
                        .find("." + CLASS_JQUERY_TREE_HANDLE)
                            .html(TREE_NODE_HANDLE_EXPANDED)
                            .addClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
                }

                updateTreeState();
            }

            function deleteNode() {
                var oldParent = this.parent();
                this.remove();

                //  TODO: убрать дубликат кода
                // Поиск li в списке, из которого перетащили элемент
                var oldBranches = $("li", oldParent);
                if (oldBranches.size() == 0) {
                    oldParent.parent()
                        .removeClass(CLASS_JQUERY_TREE_NODE)
                        .addClass(CLASS_JQUERY_TREE_LEAF);
                    // Удаление кнопки разворачивания и списка, если он оказался пустым
                    oldParent.parent().find("." + CLASS_JQUERY_TREE_HANDLE).remove();
                    oldParent.remove();
                }
            }

            function expandAll() {
                $(this).parent().next('.' + CLASS_JQUERY_TREE)
                                .find('li:has(ul)')
                                    .removeClass(CLASS_JQUERY_TREE_COLLAPSED)
                                .find('.' + CLASS_JQUERY_TREE_HANDLE)
                                    .html(TREE_NODE_HANDLE_EXPANDED)
                                    .addClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
            }

            function collapseAll() {
                $(this).parent().next('.' + CLASS_JQUERY_TREE)
                        .find('li:has(ul)')
                            .addClass(CLASS_JQUERY_TREE_COLLAPSED)
                        .find('.' + CLASS_JQUERY_TREE_HANDLE)
                            .html(TREE_NODE_HANDLE_COLLAPSED)
                            .removeClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
            }

            function changeCollapsedState() {
                var leafContainer = $(this).parent('li');
                var leafHandle = leafContainer.find('>.' + CLASS_JQUERY_TREE_HANDLE);

                leafContainer.toggleClass(CLASS_JQUERY_TREE_COLLAPSED);

                if (leafContainer.hasClass(CLASS_JQUERY_TREE_COLLAPSED)) {
                    leafHandle.html(TREE_NODE_HANDLE_COLLAPSED);
                    leafHandle.removeClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
                } else {
                    leafHandle.html(TREE_NODE_HANDLE_EXPANDED);
                    leafHandle.addClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
                }
            }

            function setDragNDrop(droppableNodes, draggableNodes) {
                $("*", tree).droppable("destroy");
                $("*", tree).draggable("destroy");

                if (!options.dragNdrop) {
                    return;
                }

                // Установка возможности бросать элементы в теги <a>
                droppableNodes.droppable({
                    tolerance   : "pointer",
                    hoverClass  : "ui-state-highlight",
                    drop        : function(event, ui) {
                        // Получение и выравнивание брошенного элемента
                        var droppedNode = ui.draggable;
                        droppedNode.css({top: 0, left: 0});

                        // Получение родителя принимаемого элемента (li)
                        var receivingNode = $(this).parent();
                        if (receivingNode == droppedNode) {
                            return;
                        }

                        // Получение родителя принимаемого элемента (li)
                        var oldParent = droppedNode.parent();

                        // Вызов обработчика события бросания элемента
                        if (options.onDrop) {
                            options.onDrop.call(droppedNode, receivingNode, oldParent.parent());
                        }

                        // Поиск списка у принимаемого li, создание при необходимости
                        var subbranch = $(receivingNode).children("ul");
                        if (subbranch.size() == 0) {
                            // Вставка кнопки перед ссылкой, так как
                            // receivingNode.prepend() добавляет еще и пробел
                            receivingNode.find("a:first")
                                .before("<span class='" + CLASS_JQUERY_TREE_HANDLE + " " + CLASS_JQUERY_TREE_HANDLE_COLLAPSE + "'>" + TREE_NODE_HANDLE_EXPANDED + "</span>");
                            receivingNode.append("<ul></ul>");
                            subbranch = receivingNode.find("ul");
                        }

                        // Перенос брошенного элемента в другой список
                        subbranch.eq(0).append(droppedNode);

                        // Добавление класса принимаемому элементу (если перед этим был листом)
                        receivingNode
                            .addClass(CLASS_JQUERY_TREE_NODE)
                            .removeClass(CLASS_JQUERY_TREE_LEAF);

                        // Поиск li в списке, из которого перетащили элемент
                        var oldBranches = $("li", oldParent);
                        if (oldBranches.size() == 0) {
                            oldParent.parent()
                                .removeClass(CLASS_JQUERY_TREE_NODE)
                                .addClass(CLASS_JQUERY_TREE_LEAF);
                            // Удаление кнопки разворачивания и списка, если он оказался пустым
                            oldParent.parent().find("." + CLASS_JQUERY_TREE_HANDLE).remove();
                            oldParent.remove();
                        }

                        updateTreeState();
                    }
                });

                // Установка возможности перетаскивать элементы дерева
                draggableNodes.draggable({
                    revert: true
                });
            }

            function setSortable() {
                if (!options.sortable) {
                    return;
                }
                
                $("ul", tree.parent()).sortable({
                    placeholder : "ui-state-highlight",
                    update      : options.onSortUpdate
                });
            }

            function setDeleteLink(selector, isRootDeletable) {
                if (!options.deletable) {
                    return;
                }

                $("." + CLASS_JQUERY_TREE_DELETE_LINK, tree).remove();

                var deletableNodes;
                if (selector) {
                    // Можно удалять узлы, задаваемые параметром
                    deletableNodes = $(tree).find(selector);
                } else {
                    if (isRootDeletable) {
                        // Можно удалять и корневые узлы тоже
                        deletableNodes = $(tree).find("li");
                    } else {
                        // Можно удалять только не корневые узлы
                        deletableNodes = $(tree).find("li li");
                    }
                }

                // Создание ссылки для удаления
                var deleteLink = options.deleteLink || "<a>Удалить</a>";
                deleteLink = $(deleteLink).addClass(CLASS_JQUERY_TREE_DELETE_LINK);

                // Вставка ссылки в узел дерева
                deletableNodes.find("." + CLASS_JQUERY_TREE_TITLE).after(deleteLink);

                // Установка обработчика на клик ссылки удаления узла
                deletableNodes.find("." + CLASS_JQUERY_TREE_DELETE_LINK)
                    .bind("click", function() {
                        var node        = $(this).parent();
                        var nodeParent  = node.parent().parent();
                        var needToDelete = true;

                        // Вызов предобработчика события удаления, если задан
                        if (options.onBeforeDelete) {
                            // Предобработчик должен подтвердить необходимость
                            // удаления узла
                            needToDelete =
                                options.onBeforeDelete.call(node, nodeParent);
                        }

                        if (needToDelete) {
                            // Удаление узла
                            deleteNode.call(node);

                            // Вызов постобработчика события удаления, если задан
                            if (options.onAfterDelete) {
                                options.onAfterDelete.call(node, nodeParent);
                            }

                            updateTreeState();
                        }
                    });
            }

            function setEditLink(selector) {
                if (!options.editable) {
                    return;
                }

                $("." + CLASS_JQUERY_TREE_EDIT_LINK, tree).remove();

                var editableNodes;
                if (selector) {
                    // Можно редактировать узлы, задаваемые параметром
                    editableNodes = $(tree).find(selector);
                } else {
                    if (options.isRootEditable) {
                        // Можно редактировать и корневые узлы тоже
                        editableNodes = $(tree).find("li");
                    } else {
                        // Можно редактировать только не корневые узлы
                        editableNodes = $(tree).find("li li");
                    }
                }

                // Создание ссылки для редактирования
                var editLink = options.editLink || "<a>Редактировать</a>";
                editLink = $(editLink).addClass(CLASS_JQUERY_TREE_EDIT_LINK);

                // Вставка ссылки в узел дерева
                editableNodes.find("." + CLASS_JQUERY_TREE_TITLE + ":first").after(editLink);

                // Установка обработчика на клик ссылки редактирования узла
                editableNodes.find("." + CLASS_JQUERY_TREE_EDIT_LINK)
                    .bind("click", function() {
                        var node        = $(this).parent();
                        var nodeParent  = node.parent().parent();
                        var needToEdit  = true;

                        // Вызов предобработчика события редактирования, если задан
                        if (options.onBeforeEdit) {
                            // Предобработчик редактирования узла
                            needToEdit =
                                options.onBeforeEdit.call(node, nodeParent);
                        }

                        if (needToEdit) {
                            // Вызов обработчика события редактирования, если задан
                            if (options.onEdit) {
                                options.onEdit.call(node.children("." + CLASS_JQUERY_TREE_TITLE), nodeParent);
                            }
                        }
                    });
            }

            function setAdditionalLinks() {
                if (!options.additionalLinks) {
                    return;
                }

                //  TODO: проверить указаны ли все необходимые параметры доп. ссылок

                for (var i = 0; i < options.additionalLinks.length; i++) {
                    // Создание ссылки
                    var additionalLink =
                            $("<a>" + options.additionalLinks[i].text + "</a>")
                                .attr("name", options.additionalLinks[i].name);

                    if (options.additionalLinks[i].cssClass) {
                        additionalLink.addClass(options.additionalLinks[i].cssClass);
                    }

                    var additionalLinksNodes;
                    if (options.additionalLinks[i].selector) {
                        additionalLinksNodes = $(tree).find(options.additionalLinks[i].selector);
                    } else {
                        additionalLinksNodes = $(tree).find("li");
                    }

                    var additionalLinksSelector =
                            "*[name=" + options.additionalLinks[i].name + "]";

                    $(additionalLinksSelector, tree).remove();

                    // Вставка ссылки в узлы дерева
                    additionalLinksNodes.find("a:first").after(additionalLink);

                    // Установка обработчика на клик ссылки
                    $(additionalLinksSelector, tree)
                        .bind("click", function() {
                            var node = $(this).parent().find("a:first");

                            for (var i = 0; i < options.additionalLinks.length; i++) {
                                // Вызов обработчика события клика ссылки
                                if ($(this).hasClass(options.additionalLinks[i].cssClass)
                                    && options.additionalLinks[i].onClick)
                                {
                                    options.additionalLinks[i].onClick.call(node);
                                }
                            }
                        });
                }
            }

            function insertNode(node) {
                if (!node.parent) {
                    node.parent = $("ul:first", tree);
                }

                if (node.tag && "a" == node.tag) {
                    if (!node.href) {
                        node.href = "javascript:void(0)";
                    }

                    node.parent.append("<li><a id='" + node.id + "' href='" + node.href + "' title='" + node.title + "'>" + node.title + "</a></li>");
                } else {
                    node.parent.append("<li><span id='" + node.id + "'>" + node.title + "</span></li>");
                }

                var $node = $(tree).find("#" + node.id).parent();

                $node.find(':first').addClass(CLASS_JQUERY_TREE_TITLE)
                        .closest('li').addClass(CLASS_JQUERY_TREE_LEAF);

                updateTreeState();
            }

            function expandNode(node) {
                node = $(node);

                node.parents("li:has(ul)")
                        .removeClass(CLASS_JQUERY_TREE_COLLAPSED)
                        .children("." + CLASS_JQUERY_TREE_HANDLE)
                            .html(TREE_NODE_HANDLE_EXPANDED)
                            .addClass(CLASS_JQUERY_TREE_HANDLE_COLLAPSE);
            }

            function updateTreeState() {
                // Установка возможности drag-n-drop
                setDragNDrop($(tree).find("li a"), $(tree).find("li"));

                setSortable();

                // Установка возможности редактирования узлов дерева
                setEditLink(options.editableSelector);

                // Установка возможности удаления узлов дерева
                setDeleteLink(options.deletableSelector, options.isRootDeletable);

                setAdditionalLinks();
            }

            if (!$(tree).hasClass(CLASS_JQUERY_TREE)) {
                // Инициализация дерева при необходимости
                init();
            }

            if (undefined != options.updateTreeState) {
                updateTreeState();
            }

            if (undefined != options.insertNode) {
                insertNode(options.insertNode);
            }

            if (undefined != options.expandNode) {
                expandNode(options.expandNode);
            }

            return $(this);
        }
    });
})(jQuery);

