/*
 *  Copyright 2024 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * This tool displays the properties of the current selected targets.
 * It concatenates several properties tool.
 * @private
 */
Ext.define('Ametys.plugins.cms.properties.PropertiesTool', {
    extend: "Ametys.tool.Tool",
    
    /**
     * @property {String[]} _hiddenSections The hidden sections, by default the technical section is hidden
     * @private
     */
    _hiddenSections: [ 'technical-section' ],

    /**
     * @property {Ametys.ui.tool.ToolPanel} _emptyTab The tab with empty text to display if no other tabs have something to display.
     * @private
     */
    _emptyTab: null,
    
    /**
     * @property {Object} _tabs The properties tab into the properties tool by id
     * @property {String} _tabs.key The "tool" id of the tab
     * @property {Ametys.tool.Tool} _tabs.value A reference to the tab, in most cases, it is a {@link Ametys.plugins.cms.properties.PropertiesTab}
     * @private
     */
    _tabs: {},
    
    /**
     * @property {Object} _tabSelectionWeight The tab weight when selected
     * @property {String} _tabSelectionWeight.key The "tool" id of the tab
     * @property {Number} _tabSelectionWeight.value The weight of the tab, more the number is high, more the tab is prior to be selected by default
     * @private
     */
    _tabSelectionWeight: {},
    
    statics: {
        /**
         * Copy value in clipboard
         * @param {Object} button The copy button.
         * @param {String} value The value to copy.
         */
        copyToClipboard: function (button, value)
        {
            let onclickEvent = button.onclick;
            if (onclickEvent != null)
            {
                // Remove the onclick to avoid multiple clicks on the button
                // Class of the button can be saved safely
                // Re-add the onclick at the end of the process
                button.onclick = null;
                
                $(document.body).append('<input type="text" id="input-text-clipboard" style="opacity: 0; position: absolute; top: -1000px;"/>');
                let clipboardInput = document.getElementById("input-text-clipboard");
                clipboardInput.value = value;
                clipboardInput.select();
                
                let spanIcon = Ext.get(button).child("span");
                let saveCls = Object.keys(spanIcon.getClassMap());
                spanIcon.removeCls(saveCls);
                
                // Use the deprecated document.execCommand because navigator.clipboard is only compatible with HTTPS
                if (document.execCommand("copy"))
                {
                    spanIcon.addCls("ametysicon-sign-raw-check");
                }
                else
                {
                    spanIcon.addCls("ametysicon-sign-raw-cross");
                }
                
                try
                {
                    clipboardInput.remove();
                }
                finally
                {
                    setTimeout(function() {
                        spanIcon.removeCls(["ametysicon-sign-raw-check", "ametysicon-sign-raw-cross"]);
                        spanIcon.addCls(saveCls);
                        button.onclick = onclickEvent;
                    }, 1000);
                }
            }
        }
    },

    constructor: function(/*config*/)
    {
        this.callParent(arguments);
        Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGING, this._onSelectionChanging, this);
    },
    
    createPanel: function ()
    {
        return Ext.create('Ext.tab.Panel', {
            tabPosition: 'top',
            tabRotation: 0,
            tabBar: {flex: 1000000}, // HACK : with such weigh, the tab will be wide and the space before the tool (flex:1) will be narrow
            tabBarHeaderPosition: 0,
            
            stateful: true,
            stateId: "AmetysPropertiesTool",
            getState: Ext.bind(this._getState, this),
            applyState: Ext.bind(this._applyState, this),
            
            tools: [
                // Show/hide technical data section
                this._createShowHideSectionButton(
                    'technical-section',
                    "{{i18n UITOOL_DETAILS_HIDE_TECHNICAL_DATA}}",
                    "{{i18n UITOOL_DETAILS_SHOW_TECHNICAL_DATA}}"
                )
            ],
            
            style: {
                margin: '5px'
            },
            header: {
                style: {
                    paddingLeft: '0px',
                    paddingRight: '0px'
                }
            },
            
            items: [],
            listeners: {
                // After render because we need the header and tabs to be accessible
                "afterrender": {
                    fn: this._lazyItemsLoad,
                    options: { single: true },
                    scope: this
                }
            }
        });
    },
    
    _createShowHideSectionButton: function(sectionName, enabledText, disabledText)
    {
        return {
            xtype: 'button',
            scope: this,
            
            height: 22,
            width: 22,
            style: {
                marginLeft: '3px'
            },
            
            enableToggle: true,
            sectionName: sectionName,
            enabledText: enabledText,
            disabledText: disabledText,
            
            iconCls: 'ametysicon-three115',
            
            toggleHandler: this._updateHideStatus,
            listeners: {
                "afterrender": {
                    fn: function(btn)
                    {
                        let showStatus = !Ext.Array.contains(this._hiddenSections, sectionName);
                        btn.toggle(showStatus);
                        // Force the toggle handler because it is called only if the value is modified.
                        // We always want to call it on first load to update the section visibility
                        // and the tooltip
                        this._updateHideStatus(btn, showStatus);
                    },
                    options: { single: true },
                    scope: this
                }
            }
        };
    },
    
    /**
     * @private
     * Load and add items to the content panel.
     * This method is called when the panel is added to its parent.
     * We do that because of conflics on listeners.
     */
    _lazyItemsLoad: function()
    {
        // Add the empty tab
        this._emptyTab = Ext.create(
            'Ametys.ui.tool.ToolPanel',
            {
                items: [
                    {
                        xtype: 'component',
                        cls: 'a-panel-text-empty',
                        border: false,
                        html: "{{i18n UITOOL_DETAILS_NO_MATCHED_SELECTION}}"
                    }
                ]
            }
        );
        
        this.getContentPanel().add(this._emptyTab);
        this._emptyTab.tab.hide();
        this.getContentPanel().getHeader().hide();
        
        // Create tab factory, needed for events
        let factory = Ext.create("Ametys.plugins.cms.properties.PropertiesTool.TabFactory", {});
        
        // Load add tabs into the properties tool
        // Add it to an array to be able to iterate on it
        // Add the wrapper of the tab as items of the main panel
        this._tabs = {};
        for (let propertiesTabConf of this.initialConfig.propertiesTab)
        {
            // Create the tab and its wrapper
            let tab = factory.openTool(propertiesTabConf);
            tab.setParentTool(this.getId());
            let wrapper = tab.createWrapper();
            
            // Adjust the configuration of the tab wrapper
            wrapper.setClosable(false);
            
            // Add tab wrapper to content panel items
            this.getContentPanel().add(wrapper);
            
            // Initialize and refresh tool
            tab.setParams({});
            
            // Add tab to registered tabs
            this._tabs[tab.id] = tab;
        }
    },
    
    getMBSelectionInteraction: function()
    {
        return Ametys.tool.Tool.MB_TYPE_LISTENING;
    },
    
    refresh: function(manual)
    {
        // Update the tabs into the current tool and not the tool itself
        if (this.isVisible())
        {
            for (let tab of Object.values(this._tabs))
            {
                if (
                    manual ||
                    tab.isOutOfDate() && !tab.isRefreshing()
                )
                {
                    tab.refresh();
                }
            }
        }
    },
    
    isOutOfDate: function()
    {
        // Check the maximum status of out dating in children
        let isOutOfDate = Ametys.tool.Tool.OOD_UPTODATE;
        for (let tab of Object.values(this._tabs))
        {
            if (tab.isOutOfDate() && tab.isOutOfDate() > isOutOfDate)
            {
                isOutOfDate = tab.isOutOfDate();
            }
        }
        return isOutOfDate;
    },
    
    onFocus: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onFocus();
        }
        
        this.callParent(arguments);
    },
    
    onBlur: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onBlur();
        }
        
        this.callParent(arguments);
    },
    
    onActivate: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onActivate();
        }
        
        this.callParent(arguments);
    },
    
    onDeactivate: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onDeactivate();
        }
        
        this.callParent(arguments);
    },
    
    onOpen: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onOpen();
        }
        
        this.callParent(arguments);
    },
    
    onClose: function(hadFocus)
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onClose(hadFocus);
        }
        
        this._tabs = null;
        this._emptyTab = null;
        this._hiddenSections = [];
        this._tabSelectionWeight = {};
        
        this.callParent(arguments);
    },
    
    onShow: function()
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab.onShow();
        }
        
        this.callParent(arguments);
    },
    
    _onBeforeManualClose: function(panel)
    {
        // Call event method on tabs into the current tool
        for (let tab of Object.values(this._tabs))
        {
            tab._onBeforeManualClose(panel);
        }
        
        this.callParent(arguments);
    },

    /**
     * Listener when the selection is going to changed. Registered only if #cfg-selection-target-id is specified.
     * Will save the selected tab.
     */
    _onSelectionChanging: function()
    {
        let contentPanel = this.getContentPanel();
        let activeTab = contentPanel.getActiveTab();
        
        // Nothing to do if the current activeTab is the empty tab
        if (activeTab.uiTool != undefined)
        {
            // By default the weight of the selected tab is set to the max value between the one is saved and the number of displayed items
            let activeTabWeight = 0;
            let maxVisibleItemsWeight = 0;
            
            contentPanel.items.each(
                function(panelItem)
                {
                    // Ignore the empty tab and tab with no data (not displayed)
                    if (panelItem.uiTool != undefined && !this._tabs[panelItem.uiTool].isEmpty())
                    {
                        activeTabWeight++;
                        
                        let tabWeight = this._tabSelectionWeight[panelItem.uiTool] || 0;
                        if (panelItem != activeTab && tabWeight > maxVisibleItemsWeight)
                        {
                            maxVisibleItemsWeight = tabWeight;
                        }
                    }
                },
                this
            );
            
            // Save the weight for opened tab if superior to saved weight
            let currentWeight = this._tabSelectionWeight[activeTab.uiTool] || 0;
            // Add one to the max visible items weight to be superior to the max item (not including the current selection)
            let maxWeight = Math.max(maxVisibleItemsWeight + 1, activeTabWeight);
            
            // If max weight is superior to current weight, update it
            if (currentWeight < maxWeight)
            {
                this._tabSelectionWeight[activeTab.uiTool] = maxWeight;
                
                // Save the state
                this.getContentPanel().saveState();
            }
        }
    },
    
    /**
     * Show the item in the tab and update the active tab
     * @param {Object} item The item
     */
    showTab: function(item)
    {
        let contentPanel = this.getContentPanel();
        
        // Show the tab when the item is showed
        item.tab.show();
        
        // Remove empty tab
        contentPanel.getHeader().show();
        
        let activeTab = contentPanel.getActiveTab();
        
        // Select the item only if it is not currently selected and the active tab has no more data or is the empty tab (no uiTool)
        // If it is the tab to show has a bigger weight than the currently selected tab, force the selection
        if (activeTab == undefined || activeTab.uiTool == undefined || this._tabSelectionWeight[item.uiTool] > this._tabSelectionWeight[activeTab.uiTool] || (activeTab != item && this._tabs[activeTab.uiTool].isEmpty()))
        {
            contentPanel.setActiveTab(item);
        }
    },
    
    /**
     * Hide the item in the tab and update the active tab
     * @param {Object} item The item
     */
    hideTab: function(item)
    {
        let contentPanel = this.getContentPanel();
        
        // Among active tabs, select the one with the biggest weight
        let newActiveTab = null;
        contentPanel.items.each(
            function(panelItem)
            {
                // Empty panel is ignored, it has no uiTool defined
                if (panelItem.uiTool != undefined)
                {
                    if (panelItem != item && (newActiveTab == null || this._tabSelectionWeight[panelItem.uiTool] > this._tabSelectionWeight[newActiveTab.uiTool]) && !this._tabs[panelItem.uiTool].isEmpty())
                    {
                        newActiveTab = panelItem;
                    }
                }
            },
            this
        );
        
        if (newActiveTab != null)
        {
            contentPanel.setActiveTab(newActiveTab);
        }
        // No more active tab
        else
        {
            contentPanel.setActiveTab(this._emptyTab);
            contentPanel.getHeader().hide();
        }
        
        // Hide the tab when the item is hidden
        item.tab.hide();
    },

    /**
     * @private
     * Load the hide status of the section.
     * @param {String} sectionName The name to identify the section
     */
    _loadSectionHideStatus: function(sectionName)
    {
        this.getContentPanel().toggleCls(`hide-${sectionName}`, Ext.Array.contains(this._hiddenSections, sectionName));
    },

    /**
     * @private
     * Load the hide status of the section.
     * @param {Ext.Button} btn The name to identify the section
     * @param {Boolea} showStatus The toggled status of the button
     */
    _updateHideStatus: function(btn, showStatus)
    {
        if (showStatus)
        {
            Ext.Array.remove(this._hiddenSections, btn.sectionName);
        }
        else
        {
            Ext.Array.include(this._hiddenSections, btn.sectionName);
        }
        
        // Hide or display sections
        this._loadSectionHideStatus(btn.sectionName);
        
        // Change the button tooltip
        btn.setTooltip(showStatus ? btn.enabledText : btn.disabledText);
        
        // Save the state
        this.getContentPanel().saveState();
    },

    /**
     * @private
     * Apply state for stateful content panel.
     */
    _applyState: function (state)
    {
        this._hiddenSections = state.hiddenSections || [];
        this._tabSelectionWeight = state.tabSelectionWeight || {};
    },

    /**
     * @private
     * Get state of the content panel buttons.
     */
    _getState: function ()
    {
        return {
            hiddenSections: this._hiddenSections,
            tabSelectionWeight: this._tabSelectionWeight
        };
    }
});