/*
 * Copyright (C) 2018 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.
 */

WI.AuditNavigationSidebarPanel = class AuditNavigationSidebarPanel extends WI.NavigationSidebarPanel
{
    constructor()
    {
        super("audit", WI.UIString("Audits"));
    }

    // Static

    static _createNavigationItemTitle()
    {
        return WI.UIString("Create", "Create @ Audit Tab Navigation Sidebar", "Title of button that creates a new audit.");
    }

    // Public

    showDefaultContentView()
    {
        let contentView = new WI.ContentView;

        if (WI.auditManager.editing) {
            let contentPlaceholder = WI.createMessageTextView(WI.UIString("Editing audits"));
            contentView.element.appendChild(contentPlaceholder);

            let descriptionElement = contentPlaceholder.appendChild(document.createElement("div"));
            descriptionElement.className = "description";
            descriptionElement.textContent = WI.UIString("Select an audit in the navigation sidebar to edit it.");

            let createAuditNavigationItem = new WI.ButtonNavigationItem("create-audit", WI.AuditNavigationSidebarPanel._createNavigationItemTitle(), "Images/Plus15.svg", 15, 15);
            createAuditNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
            createAuditNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateButtonNavigationItemClicked, this);

            let createAuditHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to create a new audit."), createAuditNavigationItem);
            createAuditHelpElement.classList.add("create-audit");
            contentPlaceholder.appendChild(createAuditHelpElement);

            let stopEditingAuditsNavigationItem = new WI.ButtonNavigationItem("stop-editing-audits", WI.UIString("Done"));
            stopEditingAuditsNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);

            let stopEditingAuditsHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to stop editing audits."), stopEditingAuditsNavigationItem);
            stopEditingAuditsHelpElement.classList.add("stop-editing-audits");
            contentPlaceholder.appendChild(stopEditingAuditsHelpElement);
        } else {
            let hasEnabledAudit = WI.auditManager.tests.length && WI.auditManager.tests.some((test) => !test.disabled && test.supported);

            let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
            contentView.element.appendChild(contentPlaceholder);

            if (hasEnabledAudit) {
                let descriptionElement = contentPlaceholder.appendChild(document.createElement("div"));
                descriptionElement.className = "description";
                descriptionElement.textContent = WI.UIString("Select an audit in the navigation sidebar to view its results.");
            }

            let importAuditNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
            importAuditNavigationItem.title = WI.UIString("Import audit or result");
            importAuditNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
            importAuditNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);

            let importAuditHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import an audit or a result."), importAuditNavigationItem);
            importAuditHelpElement.classList.add("import-audit");
            contentPlaceholder.appendChild(importAuditHelpElement);

            let startEditingAuditsNavigationItem = new WI.ButtonNavigationItem("start-editing-audits", WI.UIString("Edit"));
            startEditingAuditsNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);

            let startEditingAuditsHelpElement = WI.createNavigationItemHelp(hasEnabledAudit ? WI.UIString("Press %s to start editing audits.") : WI.UIString("Press %s to enable audits."), startEditingAuditsNavigationItem);
            startEditingAuditsHelpElement.classList.add("start-editing-audits");
            contentPlaceholder.appendChild(startEditingAuditsHelpElement);
        }

        let versionContainer = contentView.element.appendChild(document.createElement("div"));
        versionContainer.classList.add("audit-version");

        let version = WI.AuditTestBase.Version;
        if (InspectorBackend.hasDomain("Audit"))
            version = Math.min(version, InspectorBackend.getVersion("Audit"));
        versionContainer.textContent = WI.UIString("Audit version: %s").format(version);

        versionContainer.appendChild(WI.createReferencePageLink("audit-tab"));

        this.contentBrowser.showContentView(contentView);
    }

    // Popover delegate

    willDismissPopover(popover)
    {
        console.assert(popover instanceof WI.CreateAuditPopover, popover);

        let audit = popover.audit;
        if (!audit) {
            InspectorFrontendHost.beep();
            return;
        }

        WI.auditManager.addTest(audit);

        WI.showRepresentedObject(audit);
    }

    // Protected

    initialLayout()
    {
        super.initialLayout();

        this.contentTreeOutline.allowsRepeatSelection = false;

        let controlsNavigationBar = new WI.NavigationBar;

        this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("start-stop-audit", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13);
        this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
        this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this);
        controlsNavigationBar.addNavigationItem(this._startStopButtonNavigationItem);

        this._createButtonNavigationItem = new WI.ButtonNavigationItem("create-audit", WI.AuditNavigationSidebarPanel._createNavigationItemTitle(), "Images/Plus15.svg", 15, 15);
        this._createButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
        this._createButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateButtonNavigationItemClicked, this);
        controlsNavigationBar.addNavigationItem(this._createButtonNavigationItem);

        controlsNavigationBar.addNavigationItem(new WI.DividerNavigationItem);

        let importButtonNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
        importButtonNavigationItem.title = WI.UIString("Import audit or result");
        importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
        importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
        controlsNavigationBar.addNavigationItem(importButtonNavigationItem);

        this.addSubview(controlsNavigationBar);

        this._editButtonNavigationItem = new WI.ActivateButtonNavigationItem("edit-audits", WI.UIString("Edit"), WI.UIString("Done"));
        this._editButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);
        this.filterBar.addFilterNavigationItem(this._editButtonNavigationItem);

        for (let test of WI.auditManager.tests)
            this._addTest(test);

        WI.auditManager.results.forEach((result, i) => {
            this._addResult(result, i);
        });

        this._updateControlNavigationItems();
        this._updateEditNavigationItems();
        this._updateNoAuditsPlaceholder();

        WI.AuditTestGroup.addEventListener(WI.AuditTestGroup.Event.TestRemoved, this._handleAuditTestRemoved, this);

        WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this);
        WI.auditManager.addEventListener(WI.AuditManager.Event.RunningStateChanged, this._handleAuditManagerRunningStateChanged, this);
        WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
        WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
        WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
        WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this);

        this.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
    }

    closed()
    {
        super.closed();

        if (this.didInitialLayout) {
            WI.auditManager.removeEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this);
            WI.auditManager.removeEventListener(WI.AuditManager.Event.RunningStateChanged, this._handleAuditManagerRunningStateChanged, this);
            WI.auditManager.removeEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
            WI.auditManager.removeEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
            WI.auditManager.removeEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
            WI.auditManager.removeEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this);
        }
    }

    updateFilter()
    {
        super.updateFilter();

        if (!this.hasActiveFilters)
            this._updateNoAuditsPlaceholder();
    }

    hasCustomFilters()
    {
        return true;
    }

    matchTreeElementAgainstCustomFilters(treeElement, flags)
    {
        if (WI.auditManager.editing) {
            if (treeElement.representedObject instanceof WI.AuditTestResultBase || treeElement.hasAncestor(this._resultsFolderTreeElement) || treeElement === this._resultsFolderTreeElement)
                return false;
        } else {
            if (treeElement.representedObject instanceof WI.AuditTestBase && (treeElement.representedObject.disabled || !treeElement.representedObject.supported))
                return false;
        }

        return super.matchTreeElementAgainstCustomFilters(treeElement, flags);
    }

    // Private

    _addTest(test)
    {
        let treeElement = new WI.AuditTreeElement(test);

        if (this._resultsFolderTreeElement) {
            this.contentTreeOutline.insertChild(treeElement, this.contentTreeOutline.children.indexOf(this._resultsFolderTreeElement));
            this._resultsFolderTreeElement.hidden = !this._resultsFolderTreeElement.children.length;
        } else
            this.contentTreeOutline.appendChild(treeElement);
    }

    _addResult(result, index)
    {
        this.element.classList.add("has-results");

        if (!this._resultsFolderTreeElement) {
            this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results"));
            this.contentTreeOutline.appendChild(this._resultsFolderTreeElement);
        }

        this._resultsFolderTreeElement.expand();

        let resultFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Run %d").format(index + 1));
        if (result instanceof WI.AuditTestResultBase) {
            resultFolderTreeElement.subtitle = WI.UIString("Imported");
            result = [result];
        }
        this._resultsFolderTreeElement.appendChild(resultFolderTreeElement);

        console.assert(this._resultsFolderTreeElement.children.length === WI.auditManager.results.length);

        for (let resultItem of result)
            resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem));
    }

    _updateControlNavigationItems()
    {
        this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping;
        this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.some((test) => !test.disabled && test.supported) && (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive || WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
        this._startStopButtonNavigationItem.hidden = WI.auditManager.editing;

        this._createButtonNavigationItem.hidden = !WI.auditManager.editing;
    }

     _updateEditNavigationItems()
    {
        this._editButtonNavigationItem.label = WI.auditManager.editing ? this._editButtonNavigationItem.activatedToolTip : this._editButtonNavigationItem.defaultToolTip;
        this._editButtonNavigationItem.activated = WI.auditManager.editing;
        this._editButtonNavigationItem.enabled = WI.auditManager.editing || WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive;
    }

    _updateNoAuditsPlaceholder()
    {
        if (WI.auditManager.editing || WI.auditManager.tests.some((test) => !test.disabled && test.supported)) {
            if (!this.hasActiveFilters)
                this.hideEmptyContentPlaceholder();
            return;
        }

        let contentPlaceholder = this.showEmptyContentPlaceholder(WI.UIString("No Enabled Audits"));
        contentPlaceholder.classList.add("no-enabled-audits");

        if (WI.auditManager.results.length) {
            // Move the placeholder to be the first element in the content area, where it will
            // be styled so that it doesn't obstruct the results elements.
            this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild);
        }
    }

    _handleAuditManagerEditingChanged(event)
    {
        let previousSelectedTreeElement = this.contentTreeOutline.selectedTreeElement;
        if (previousSelectedTreeElement) {
            if (WI.auditManager.editing) {
                if (!(previousSelectedTreeElement.representedObject instanceof WI.AuditTestBase))
                    previousSelectedTreeElement.deselect();
            } else {
                if (previousSelectedTreeElement.representedObject.disabled || !previousSelectedTreeElement.representedObject.supported)
                    previousSelectedTreeElement.deselect();
            }
        }

        this.updateFilter();

        if (!this.contentTreeOutline.selectedTreeElement)
            this.showDefaultContentView();

        this._updateControlNavigationItems();
        this._updateEditNavigationItems();
        this._updateNoAuditsPlaceholder();
    }

    _handleAuditManagerRunningStateChanged(event)
    {
        this._updateControlNavigationItems();
        this._updateEditNavigationItems();
    }

    _handleAuditTestAdded(event)
    {
        let {test} = event.data;

        this._addTest(test);

        this._updateControlNavigationItems();
        this._updateNoAuditsPlaceholder();
    }

    _handleAuditTestCompleted(event)
    {
        let {result, index} = event.data;
        this._addResult(result, index);

        this._updateControlNavigationItems();
        this._updateEditNavigationItems();
    }

    _handleAuditTestRemoved(event)
    {
        console.assert(WI.auditManager.editing);

        let {test} = event.data;

        let treeElement = this.treeElementForRepresentedObject(test);
        treeElement.parent.removeChild(treeElement);

        this._updateControlNavigationItems();
    }

    _handleAuditTestScheduled(event)
    {
        this._updateControlNavigationItems();
        this._updateEditNavigationItems();
    }

    _treeSelectionDidChange(event)
    {
        if (!this.selected)
            return;

        let treeElement = this.contentTreeOutline.selectedTreeElement;
        if (!treeElement || treeElement instanceof WI.FolderTreeElement) {
            this.showDefaultContentView();
            return;
        }

        let representedObject = treeElement.representedObject;
        if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
            || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) {
            WI.showRepresentedObject(representedObject);
            return;
        }

        console.error("Unknown tree element", treeElement);
    }

    _handleStartStopButtonNavigationItemClicked(event)
    {
        if (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive)
            WI.auditManager.start();
        else if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active)
            WI.auditManager.stop();
    }

    _handleCreateButtonNavigationItemClicked(event)
    {
        console.assert(WI.auditManager.editing);

        let popover = new WI.CreateAuditPopover(this);
        popover.show(event.target.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MAX_X, WI.RectEdge.MIN_X]);
    }

    _handleImportButtonNavigationItemClicked(event)
    {
        WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result), {multiple: true});
    }

    _handleEditButtonNavigationItemClicked(event)
    {
        WI.auditManager.editing = !WI.auditManager.editing;
    }
};
