/*
 * Copyright (C) 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

WebInspector.GeneralTreeElement = function(classNames, title, subtitle, representedObject, hasChildren)
{
    TreeElement.call(this, "", representedObject, hasChildren);

    this.classNames = classNames;

    this._tooltipHandledSeparately = false;
    this._mainTitle = title || "";
    this._subtitle = subtitle || "";
    this._status = "";
}

WebInspector.GeneralTreeElement.StyleClassName = "item";
WebInspector.GeneralTreeElement.DisclosureButtonStyleClassName = "disclosure-button";
WebInspector.GeneralTreeElement.IconElementStyleClassName = "icon";
WebInspector.GeneralTreeElement.StatusElementStyleClassName = "status";
WebInspector.GeneralTreeElement.TitlesElementStyleClassName = "titles";
WebInspector.GeneralTreeElement.MainTitleElementStyleClassName = "title";
WebInspector.GeneralTreeElement.SubtitleElementStyleClassName = "subtitle";
WebInspector.GeneralTreeElement.NoSubtitleStyleClassName = "no-subtitle";
WebInspector.GeneralTreeElement.SmallStyleClassName = "small";
WebInspector.GeneralTreeElement.TwoLineStyleClassName = "two-line";

WebInspector.GeneralTreeElement.Event = {
    MainTitleDidChange: "general-tree-element-main-title-did-change"
};

WebInspector.GeneralTreeElement.prototype = {
    constructor: WebInspector.GeneralTreeElement,

    // Public

    get element()
    {
        return this._listItemNode;
    },

    get disclosureButton()
    {
        this._createElementsIfNeeded();
        return this._disclosureButton;
    },

    get iconElement()
    {
        this._createElementsIfNeeded();
        return this._iconElement;
    },

    get titlesElement()
    {
        this._createElementsIfNeeded();
        return this._titlesElement;
    },

    get mainTitleElement()
    {
        this._createElementsIfNeeded();
        return this._mainTitleElement;
    },

    get subtitleElement()
    {
        this._createElementsIfNeeded();
        this._createSubtitleElementIfNeeded();
        return this._subtitleElement;
    },

    get classNames()
    {
        return this._classNames;
    },

    set classNames(x)
    {
        if (this._listItemNode && this._classNames) {
            for (var i = 0; i < this._classNames.length; ++i)
                this._listItemNode.classList.remove(this._classNames[i]);
        }

        if (typeof x === "string")
            x = [x];

        this._classNames = x || [];

        if (this._listItemNode) {
            for (var i = 0; i < this._classNames.length; ++i)
                this._listItemNode.classList.add(this._classNames[i]);
        }
    },

    addClassName: function(className)
    {
        if (this._classNames.contains(className))
            return;

        this._classNames.push(className);

        if (this._listItemNode)
            this._listItemNode.classList.add(className);
    },

    removeClassName: function(className)
    {
        if (!this._classNames.contains(className))
            return;

        this._classNames.remove(className);

        if (this._listItemNode)
            this._listItemNode.classList.remove(className);
    },

    get small()
    {
        return this._small;
    },

    set small(x)
    {
        this._small = x;

        if (this._listItemNode) {
            if (this._small)
                this._listItemNode.classList.add(WebInspector.GeneralTreeElement.SmallStyleClassName);
            else
                this._listItemNode.classList.remove(WebInspector.GeneralTreeElement.SmallStyleClassName);
        }
    },

    get twoLine()
    {
        return this._twoLine;
    },

    set twoLine(x)
    {
        this._twoLine = x;

        if (this._listItemNode) {
            if (this._twoLine)
                this._listItemNode.classList.add(WebInspector.GeneralTreeElement.TwoLineStyleClassName);
            else
                this._listItemNode.classList.remove(WebInspector.GeneralTreeElement.TwoLineStyleClassName);
        }
    },

    get mainTitle()
    {
        return this._mainTitle;
    },

    set mainTitle(x)
    {
        this._mainTitle = x || "";
        this._updateTitleElements();
        this.didChange();
        this.dispatchEventToListeners(WebInspector.GeneralTreeElement.Event.MainTitleDidChange);
    },

    get subtitle()
    {
        return this._subtitle;
    },

    set subtitle(x)
    {
        this._subtitle = x || "";
        this._updateTitleElements();
        this.didChange();
    },

    get status()
    {
        return this._status;
    },

    set status(x)
    {
        this._status = x || "";
        this._updateStatusElement();
    },

    get filterableData()
    {
        return {text: [this.mainTitle, this.subtitle]};
    },

    get tooltipHandledSeparately()
    {
        return this._tooltipHandledSeparately;
    },

    set tooltipHandledSeparately(x)
    {
        this._tooltipHandledSeparately = x || false;
    },

    // Overrides from TreeElement (Private)

    isEventWithinDisclosureTriangle: function(event)
    {
        return event.target === this._disclosureButton;
    },

    onattach: function()
    {
        this._createElementsIfNeeded();
        this._updateTitleElements();
        this._updateStatusElement();

        this._listItemNode.classList.add(WebInspector.GeneralTreeElement.StyleClassName);

        if (this._classNames) {
            for (var i = 0; i < this._classNames.length; ++i)
                this._listItemNode.classList.add(this._classNames[i]);
        }

        if (this._small)
            this._listItemNode.classList.add(WebInspector.GeneralTreeElement.SmallStyleClassName);

        if (this._twoLine)
            this._listItemNode.classList.add(WebInspector.GeneralTreeElement.TwoLineStyleClassName);

        this._listItemNode.appendChild(this._disclosureButton);
        this._listItemNode.appendChild(this._iconElement);
        this._listItemNode.appendChild(this._statusElement);
        this._listItemNode.appendChild(this._titlesElement);

        if (this.oncontextmenu && typeof this.oncontextmenu === "function") {
            this._boundContextMenuEventHandler = this.oncontextmenu.bind(this);
            this._listItemNode.addEventListener("contextmenu", this._boundContextMenuEventHandler, true);
        }

        if (!this._boundContextMenuEventHandler && this.treeOutline.oncontextmenu && typeof this.treeOutline.oncontextmenu === "function") {
            this._boundContextMenuEventHandler = function(event) { this.treeOutline.oncontextmenu(event, this); }.bind(this);
            this._listItemNode.addEventListener("contextmenu", this._boundContextMenuEventHandler, true);
        }
    },

    ondetach: function()
    {
        if (this._boundContextMenuEventHandler) {
            this._listItemNode.removeEventListener("contextmenu", this._boundContextMenuEventHandler, true);
            delete this._boundContextMenuEventHandler;
        }
    },

    onreveal: function()
    {
        if (this._listItemNode)
            this._listItemNode.scrollIntoViewIfNeeded(false);
    },

    // Protected

    callFirstAncestorFunction: function(functionName, args)
    {
        // Call the first ancestor that implements a function named functionName (if any).
        var currentNode = this.parent;
        while (currentNode) {
            if (typeof currentNode[functionName] === "function") {
                currentNode[functionName].apply(currentNode, args);
                break;
            }

            currentNode = currentNode.parent;
        }
    },

    // Private

    _createElementsIfNeeded: function()
    {
        if (this._createdElements)
            return;

        this._disclosureButton = document.createElement("button");
        this._disclosureButton.className = WebInspector.GeneralTreeElement.DisclosureButtonStyleClassName;

        // Don't allow the disclosure button to be keyboard focusable. The TreeOutline is focusable and has
        // its own keybindings for toggling expand and collapse.
        this._disclosureButton.tabIndex = -1;

        this._iconElement = document.createElement("img");
        this._iconElement.className = WebInspector.GeneralTreeElement.IconElementStyleClassName;

        this._statusElement = document.createElement("div");
        this._statusElement.className = WebInspector.GeneralTreeElement.StatusElementStyleClassName;

        this._titlesElement = document.createElement("div");
        this._titlesElement.className = WebInspector.GeneralTreeElement.TitlesElementStyleClassName;

        this._mainTitleElement = document.createElement("span");
        this._mainTitleElement.className = WebInspector.GeneralTreeElement.MainTitleElementStyleClassName;
        this._titlesElement.appendChild(this._mainTitleElement);

        this._createdElements = true;
    },

    _createSubtitleElementIfNeeded: function()
    {
        if (this._subtitleElement)
            return;

        this._subtitleElement = document.createElement("span");
        this._subtitleElement.className = WebInspector.GeneralTreeElement.SubtitleElementStyleClassName;
        this._titlesElement.appendChild(this._subtitleElement);
    },

    _updateTitleElements: function()
    {
        if (!this._createdElements)
            return;

        if (typeof this._mainTitle === "string") {
            if (this._mainTitleElement.textContent !== this._mainTitle)
                this._mainTitleElement.textContent = this._mainTitle;
        } else if (this._mainTitle instanceof Node) {
            this._mainTitleElement.removeChildren();
            this._mainTitleElement.appendChild(this._mainTitle);
        }

        if (typeof this._subtitle === "string" && this._subtitle) {
            this._createSubtitleElementIfNeeded();
            if (this._subtitleElement.textContent !== this._subtitle)
                this._subtitleElement.textContent = this._subtitle;
            this._titlesElement.classList.remove(WebInspector.GeneralTreeElement.NoSubtitleStyleClassName);
        } else if (this._subtitle instanceof Node) {
            this._createSubtitleElementIfNeeded();
            this._subtitleElement.removeChildren();
            this._subtitleElement.appendChild(this._subtitle);
        } else {
            if (this._subtitleElement)
                this._subtitleElement.textContent = "";
            this._titlesElement.classList.add(WebInspector.GeneralTreeElement.NoSubtitleStyleClassName);
        }

        // Set a default tooltip if there isn't a custom one already assigned.
        if (!this.tooltip && !this._tooltipHandledSeparately) {
            console.assert(this._listItemNode);

            // Get the textContent for the elements since they can contain other nodes,
            // and the tool tip only cares about the text.
            var mainTitleText = this._mainTitleElement.textContent;
            var subtitleText = this._subtitleElement ? this._subtitleElement.textContent : "";

            if (mainTitleText && subtitleText)
                this._listItemNode.title = mainTitleText + (this._small && !this._twoLine ? " \u2014 " : "\n") + subtitleText;
            else if (mainTitleText)
                this._listItemNode.title = mainTitleText;
            else
                this._listItemNode.title = subtitleText;
        }
    },

    _updateStatusElement: function()
    {
        if (!this._createdElements)
            return;

        if (this._status instanceof Node) {
            this._statusElement.removeChildren();
            this._statusElement.appendChild(this._status);
        } else
            this._statusElement.textContent = this._status;
    }
}

WebInspector.GeneralTreeElement.prototype.__proto__ = TreeElement.prototype;
