///////////////////////////////////////////////////////////////////////////
// Copyright © Esri. All Rights Reserved.
//
// 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.
///////////////////////////////////////////////////////////////////////////

define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/array',
    'dojo/_base/html',
    'dojo/aspect',
    'dojo/query',
    'dojo/on',
    'dojo/keys',
    'dijit/Tooltip',
    'dojo/Deferred',
    'dojo/mouse',
    'dojo/dom-construct',
    'dojo/dom-geometry',
    'jimu/BaseWidget',
    'jimu/PoolControllerMixin',
    'jimu/tokenUtils',
    'jimu/portalUtils',
    'jimu/portalUrlUtils',
    'jimu/utils',
    'jimu/dijit/Message',
    './PopupTileNodes',
    'dojo/NodeList-manipulate'
  ],
  function(declare, lang, array, html, aspect, query, on, keys, Tooltip, Deferred, mouse,
    domConstruct, domGeometry, BaseWidget, PoolControllerMixin, tokenUtils, portalUtils,
    portalUrlUtils, utils, Message, PopupTileNodes) {
    /* global jimuConfig */
    /* jshint scripturl:true */
    var clazz = declare([BaseWidget, PoolControllerMixin], {

      baseClass: 'jimu-widget-header-controller jimu-main-background',

      maxIconCount: -1,

      //whether need to create more icon
      createMoreIcon: false,

      //title, links are switchable depends the browser width
      switchableElements: {},

      //the default height of the widget
      height: 40,

      //the opened group/widget's id
      openedId: '',

      moveTopOnActive: false,

      postMixInProperties: function() {
        this.inherited(arguments);
        this.isRenderIdForAttrs = true;
      },

      postCreate: function() {
        this.inherited(arguments);
        html.setAttr(this.domNode, 'aria-label', this.nls._widgetLabel);

        this._processGroupSetting();

        this.switchableElements.title = this.titleNode;
        this.switchableElements.links = this.linksNode;
        this.switchableElements.subtitle = this.subtitleNode;

        this._handleTitleColorAndLogoLink(this.appConfig);

        if (this.position && this.position.height) {
          this.height = this.position.height;
        }

        // if (!this.appConfig.portalUrl) {
        html.setStyle(this.signInSectionNode, 'display', 'none');
        // } else {
        //   html.setStyle(this.signInSectionNode, 'display', '');
        // }

        if (this.appConfig && this.appConfig.logo) {
          this.logoNode.src = this.appConfig.logo;
          html.removeClass(this.logoNode, 'hide-logo');
        } else {
          this.logoNode.src = "";
          html.addClass(this.logoNode, 'hide-logo');
        }

        this.switchableElements.title.innerHTML =
          utils.sanitizeHTML(this.appConfig.title ? this.appConfig.title : '');
        this.switchableElements.subtitle.innerHTML =
          utils.sanitizeHTML(this.appConfig.subtitle ? this.appConfig.subtitle : '');

        this._createDynamicLinks(this.appConfig.links);

        this._setElementsSize();

        this.own(on(this.domNode, mouse.enter, lang.hitch(this, function() {
          var title = '';
          var portalUrl = this.appConfig && this.appConfig.portalUrl || '';
          var server = portalUrlUtils.getServerByUrl(portalUrl);
          if (portalUrlUtils.isArcGIScom(server)) {
            server = 'ArcGIS.com';
          }
          if (server) {
            title = this.nls.signInTo + ' ' + server;
          }
          this.signinLinkNode.title = title;
        })));
      },

      startup: function() {
        this.inherited(arguments);
        this.resize();
        setTimeout(lang.hitch(this, this.resize), 100);
      },

      onAction: function(action, data) {
        /*jshint unused: false*/
        if (action === 'highLight' && data) {
          var node = query('div[settingid="' + data.widgetId + '"]', this.domNode)[0];
          this._highLight(node);
        }
        if (action === 'removeHighLight') {
          this._removeHighLight();
        }
      },

      onSignIn: function(credential) {
        this.inherited(arguments);

        html.setStyle(this.signinLinkNode, 'display', 'none');
        html.setStyle(this.userNameLinkNode, 'display', '');
        html.setStyle(this.signoutLinkNode, 'display', '');

        this.userNameLinkNode.innerHTML = credential.userId;
        html.setAttr(this.userNameLinkNode, 'href', this.appConfig.portalUrl + 'home/user.html');

        //popup
        if (this.popupLinkNode) {
          html.setStyle(this.popupSigninNode, 'display', 'none');
          html.setStyle(this.popupUserNameNode, 'display', '');
          html.setStyle(this.popupSignoutNode, 'display', '');

          query('a', this.popupUserNameNode).html(credential.userId)
            .attr('href', this.appConfig.portalUrl + 'home/user.html');
        }
        this.resize();
      },

      onSignOut: function() {
        this.inherited(arguments);

        this._onSignOut(this.nls.signin);

        var portal = portalUtils.getPortal(this.appConfig.portalUrl);
        portal.loadSelfInfo().then(lang.hitch(this, function(selfInfo) {
          var name = selfInfo.name;
          var signInTip = this.nls.signInTo + ' ' + name;
          this._onSignOut(signInTip);
        }), lang.hitch(this, function(err) {
          console.error(err);
        }));
      },

      _onSignOut: function(signInTip) {
        html.setStyle(this.signinLinkNode, 'display', '');
        html.setAttr(this.signinLinkNode, 'innerHTML', signInTip);
        html.setStyle(this.userNameLinkNode, 'display', 'none');
        html.setStyle(this.signoutLinkNode, 'display', 'none');

        this.userNameLinkNode.innerHTML = '';

        //popup
        if (this.popupLinkNode) {
          html.setStyle(this.popupSigninNode, 'display', '');
          html.setAttr(this.popupSigninNode, 'innerHTML', signInTip);
          html.setStyle(this.popupUserNameNode, 'display', 'none');
          html.setStyle(this.popupSignoutNode, 'display', 'none');

          query('a', this.popupUserNameNode).html('');
        }

        this.resize();
      },

      resize: function() {
        this._resize();
      },

      _resize: function() {
        var box = html.getContentBox(this.domNode);

        //by default, we show all elements
        this._showSwitchableElements(['title', 'links', 'subtitle']);

        // this.timeoutHandle = setTimeout(lang.hitch(this, function(){
        //   this._createIconNodes(box);
        // }), 50);

        this._createIconNodes(box);

        if (this.morePane) {
          this.morePane.resize();
        }
        if (this.popupLinkNode) {
          html.setStyle(jimuConfig.layoutId, {
            left: html.getContentBox(this.popupLinkNode).w + 'px'
          });
        }
      },

      destroy: function() {
        if (this.timeoutHandle) {
          clearTimeout(this.timeoutHandle);
          this.timeoutHandle = null;
        }
        if (this.morePane) {
          this.morePane.destroy();
        }
        if (this.moreIconPaneCoverNode) {
          html.destroy(this.moreIconPaneCoverNode);
          this.moreIconPaneCoverNode = null;
        }
        if (this.popupLinkNode && this.popupLinksVisible) {
          this._hidePopupLink();
        }
        html.destroy(this.popupLinkNode);
        this.inherited(arguments);
      },

      onAppConfigChanged: function(appConfig, reason, changedData) {
        switch (reason) {
          case 'attributeChange':
            this._onAttributeChange(appConfig, changedData);
            break;
          default:
            return;
        }
        this.appConfig = appConfig;
        this.resize();
      },

      getOpenedIds: function() {
        this.inherited(arguments);
        if (this.openedId === '') {
          return [];
        }
        return [this.openedId];
      },

      setOpenedIds: function(ids) {
        if (ids.length === 0) {
          return;
        }
        var config = this.getConfigById(ids[0]);
        if (!config) {
          return;
        }
        if(this.openedId){
          this._switchNodeToClose(this.openedId);
        }
        this.openedId = ids[0];
        if (config.widgets && config.openType === 'openAll') {
          this._switchNodeToOpen(config.id);
        } else if (!config.widgets) {
          if(this._getIconNodeById(config.id)){
            this._switchNodeToOpen(config.id);
          }else{
            this._showIconContent(config);
          }
        }
      },

      _onLogoLoad: function() {
        this.resize();
      },

      _highLight: function(node) {
        if (this.hlDiv) {
          this._removeHighLight();
        }
        if (!node) {
          return;
        }
        var position = domGeometry.getMarginBox(node);
        var hlStyle = {
          position: 'absolute',
          left: (position.l) + 'px',
          top: (position.t) + 'px',
          width: (position.w) + 'px',
          height: (position.h) + 'px'
        };
        this.hlDiv = domConstruct.create('div', {
          "style": hlStyle,
          "class": 'icon-highlight'
        }, node, 'before');
      },

      _removeHighLight: function() {
        if (this.hlDiv) {
          domConstruct.destroy(this.hlDiv);
          this.hlDiv = null;
        }
      },


      _onAttributeChange: function(appConfig, changedData) {
        /*jshint unused: false*/
        if ('title' in changedData && changedData.title !== this.appConfig.title) {
          this.titleNode.innerHTML = utils.sanitizeHTML(changedData.title);
        }
        if ('subtitle' in changedData && changedData.subtitle !== this.appConfig.subtitle) {
          this.subtitleNode.innerHTML = utils.sanitizeHTML(changedData.subtitle);
        }
        if ('logo' in changedData && changedData.logo !== this.appConfig.logo) {
          if(changedData.logo){
            html.setAttr(this.logoNode, 'src', changedData.logo);
            html.removeClass(this.logoNode, 'hide-logo');
          }else{
            html.removeAttr(this.logoNode, 'src');
            html.addClass(this.logoNode, 'hide-logo');
          }
        }

        if ('links' in changedData) {
          this._createDynamicLinks(changedData.links);
        }

        this._handleTitleColorAndLogoLink(appConfig);
      },

      _handleTitleColorAndLogoLink: function(appConfig){
        if(appConfig.titleColor){
          html.setStyle(this.titleNode, 'color', appConfig.titleColor);
        }else{
          html.setStyle(this.titleNode, 'color', '');
        }

        utils.themesHeaderLogoA11y(appConfig, this.tabIndex, {
          link: this.logoLinkNode,
          logo: this.logoNode,
          icon: this.logoNode
        });
      },

      _setElementsSize: function() {
        html.setStyle(this.logoNode, {
          height: '30px'
          // minWidth: '30px',
          // marginTop: ((this.height - 30) / 2) + 'px'
        });

        html.setStyle(this.titleNode, {
          lineHeight: this.height + 'px'
        });

        html.setStyle(this.subtitleNode, {
          lineHeight: this.height + 'px'
        });

        query('.jimu-link', this.domNode).style({
          lineHeight: this.height - 10 + 'px'
        });
      },

      _processGroupSetting: function() {
        function getOpenType(gLabel) {
          if(this.config.groupSetting){
            for (var i = 0; i < this.config.groupSetting.length; i++) {
              if (this.config.groupSetting[i].label === gLabel) {
                return this.config.groupSetting[i].type;
              }
            }
          }
          //this is the default open type
          return 'openAll';
        }
        array.forEach(this.appConfig.widgetPool.groups, function(g) {
          g.openType = getOpenType.call(this, g.label);
        }, this);
      },

      _createDynamicLinks: function(links) {
        html.empty(this.dynamicLinksNode);
        array.forEach(links, function(link) {
          html.create('a', {
            href: link.url,
            target: '_blank',
            rel: 'noopener noreferrer',
            innerHTML: utils.sanitizeHTML(link.label),
            'class': "jimu-link jimu-align-leading jimu-leading-margin1",
            style: {
              lineHeight: this.height + 'px'
            },
            'tabindex': this.tabIndex
          }, this.dynamicLinksNode);
        }, this);
      },

      _showSwitchableElements: function(showElement) {
        var es = this.switchableElements;

        for (var p in es) {
          if (es.hasOwnProperty(p)) {
            if (showElement.indexOf(p) > -1) {
              var displayValue = html.hasClass(es[p], 'links') ? 'flex' : 'block';
              html.setStyle(es[p], 'display', displayValue);
              es[p].visible = true;
            } else {
              html.setStyle(es[p], 'display', 'none');
              es[p].visible = false;
            }
          }
        }
        //links is hidden
        if (this.logoClickHandle) {
          this.logoClickHandle.remove();
        }
        if (this.logoKeydownHandle) {
          this.logoKeydownHandle.remove();
        }

        if (showElement.indexOf('links') < 0) {
          html.setAttr(this.logoLinkNode, 'tabIndex', this.tabIndex);
          this.logoClickHandle = on(this.logoNode, 'click', lang.hitch(this, this._onLogoClick));
          this.logoKeydownHandle = on(this.logoLinkNode, 'keydown', lang.hitch(this, this._onLogoKeydown));
          // html.setStyle(this.logoNode, {
          //   cursor: 'pointer'
          // });
        } else {
          if (this.popupLinksVisible) {
            this._hidePopupLink();
          }
          // html.setStyle(this.logoNode, {
          //   cursor: 'default'
          // });
        }
      },

      _switchSignin: function() {
        var credential = tokenUtils.getPortalCredential(this.appConfig.portalUrl);
        if (credential) {
          this.onSignIn(credential);
        } else {
          this.onSignOut();
        }
      },

      _onLogoClick: function() {
        // return;
        if (this.popupLinkNode) {
          html.destroy(this.popupLinkNode);
          // this._switchSignin();
        }
        this.popupLinkNode = this._createPopupLinkNode();

        if (this.popupLinksVisible) {
          this._hidePopupLink();
        } else {
          this._showPopupLink();
        }
      },

      _onLogoKeydown: function(evt) {
        if(evt.keyCode === keys.ENTER){
          this._onLogoClick();
        }
      },

      _hidePopupLink: function() {
        html.setStyle(this.popupLinkNode, 'display', 'none');

        if (window.isRTL) {
          html.setStyle(jimuConfig.layoutId, {
            right: 0
          });
        } else {
          html.setStyle(jimuConfig.layoutId, {
            left: 0
          });
        }

        this.popupLinksVisible = false;
      },

      _showPopupLink: function() {
        html.setStyle(this.popupLinkNode, 'display', '');

        if (window.isRTL) {
          html.setStyle(jimuConfig.layoutId, {
            right: html.getContentBox(this.popupLinkNode).w + 'px'
          });
        } else {
          html.setStyle(jimuConfig.layoutId, {
            left: html.getContentBox(this.popupLinkNode).w + 'px'
          });
        }

        //focus on first link A or close btn
        if(utils.isInNavMode()){
          if(this.appConfig.links){
            var firstLinkA = query('.jimu-link a', this.popupLinkNode)[0];
            firstLinkA.focus();
          }else{
            var closeBtn = query('.jimu-popup-link-close-btn', this.popupLinkNode)[0];
            closeBtn.focus();
          }
        }

        this.popupLinksVisible = true;
      },

      _createPopupLinkNode: function() {
        var node, titleNode, box;
        box = html.getContentBox(jimuConfig.mainPageId);

        node = html.create('div', {
          'class': 'popup-links jimu-main-background',
          style: {
            position: 'absolute',
            zIndex: 100,
            top: 0,
            bottom: 0
          }
        }, jimuConfig.mainPageId);

        if (window.isRTL) {
          html.setStyle(node, {
            right: 0,
            left: '50px'
          });
        } else {
          html.setStyle(node, {
            left: 0,
            right: '50px'
          });
        }

        titleNode = html.create('div', {
          'class': 'popup-title',
          style: {
            height: this.height + 'px',
            width: '100%'
          }
        }, node);

        var closeBtn = html.create('div', {
          'class': 'jimu-float-leading jimu-leading-margin1 jimu-popup-link-close-btn',
          'role': 'button',
          'aria-label': window.jimuNls.common.close,
          'tabindex': '0',
          style: {
            width: '30px',
            height: '30px',
            marginTop: ((this.height - 30) / 2) + 'px'
          }
        }, titleNode);
        this.own(on(closeBtn, 'click', lang.hitch(this, function() {
          this._hidePopupLink();
        })));
        this.own(on(closeBtn, 'keydown', lang.hitch(this, function(evt) {
          if(utils.isInNavMode()){
            if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE || evt.keyCode === keys.ESCAPE){
              this._hidePopupLink();
              this.logoLinkNode.focus();
            }else if(evt.keyCode === keys.TAB && !evt.shiftKey && this.appConfig.links.length > 0){
            }else{
              evt.preventDefault();
            }
          }
        })));

        this.own(on(node, 'keydown', lang.hitch(this, function(evt) {
          if(utils.isInNavMode() && evt.keyCode === keys.ESCAPE && !html.hasClass(node, 'jimu-popup-link-close-btn')){
            closeBtn.focus();
          }
        })));

        var textNode = html.create('div', {
          'class': 'title jimu-float-leading jimu-leading-margin1 jimu-ellipsis',
          innerHTML: utils.sanitizeHTML(this.appConfig.title),
          style: {
            lineHeight: this.height + 'px'
          }
        }, titleNode);

        var titleWidth = 'auto';
        try{
          titleWidth = html.getMarginBox(titleNode).w -
            html.getMarginBox(closeBtn).w -
            html.getMarginExtents(textNode).w + 'px';
        } catch(err) {
          console.error(err);
          titleWidth = 'auto';
        }
        html.setStyle(textNode, 'width', titleWidth);

        var linkNodes = [];
        array.forEach(this.appConfig.links, function(link) {
          var linkNode = this._createLinkNode(node, link, false);
          linkNodes.push(linkNode);
        }, this);
        if(linkNodes.length){
          this.own(on(linkNodes[linkNodes.length - 1], 'keydown', lang.hitch(this, function(evt) {
            if(utils.isInNavMode() && evt.keyCode === keys.TAB && !evt.shiftKey){
              evt.preventDefault();
              var linkA = query('a', linkNodes[0])[0];
              linkA.focus();
            }
          })));
        }

        // this.popupSigninNode = this._createLinkNode(node, {
        //   label: 'SignIn',
        //   url: '#'
        // }, true);
        // this.popupUserNameNode = this._createLinkNode(node, {
        //   label: '',
        //   url: '#'
        // }, true);
        // this.popupSignoutNode = this._createLinkNode(node, {
        //   label: 'SignOut',
        //   url: '#'
        // }, true);

        // this.own(on(this.popupSigninNode, 'click', lang.hitch(this, '_onSigninClick')));
        // this.own(on(this.popupSignoutNode, 'click', lang.hitch(this, '_onSignoutClick')));

        //empty
        // this._createLinkNode(node, {
        //   label: '',
        //   url: '#'
        // }, false);
        return node;
      },

      _createLinkNode: function(containerNode, link, isSign) {
        var node, lineNode, linkSectionNode, className;

        node = html.place('<div class="jimu-link"></div>', containerNode);

        lineNode = html.place('<div class="line"></div>', node);
        if (isSign) {
          className = 'link-section signin';
        } else {
          className = 'link-section';
        }
        linkSectionNode = html.place('<div class="' + className + '"></div>', node);
        html.create('a', {
          href: link.url,
          'tabindex': '0',
          'class': 'jimu-ellipsis',
          target: '_blank',
          rel: "noopener noreferrer",
          innerHTML: utils.sanitizeHTML(link.label),
          title: link.label,
          style: {
            lineHeight: '66px'
          }
        }, linkSectionNode);

        return node;
      },

      _onSigninClick: function() {
        tokenUtils.signInPortal(this.appConfig.portalUrl, this.appConfig.appId);
      },

      _onSignoutClick: function() {
        var isDepolyedApp = !this.appConfig.mode;

        if (isDepolyedApp) {
          //tokenUtils.signOutPortal(this.appConfig.portalUrl);
          tokenUtils.signOutAll();
        } else {
          new Message({
            message: this.nls.cantSignOutTip
          });
        }
      },

      _onUserNameClick: function() {

      },

      _getHeaderSectionWidth: function() {
        var width;
        width = html.getMarginBox(this.headerNode).w;
        return width;
      },

      _getContainerWidth: function(box) {
        var headSectionWidth = this._getHeaderSectionWidth();
        //the container width
        var containerWidth = box.w - headSectionWidth - this._getEmptyWidth(box);

        return containerWidth;
      },

      _calcContainerAndEmptyWidth: function(box) {
        var containerWidth = this._getContainerWidth(box);
        var emptyWidth = this._getEmptyWidth(box);
        //here, we need put at least two icons
        if (containerWidth < this.iconWidth * 2) {
          if (this.switchableElements.subtitle.visible) {
            //hiden subtitle first
            this._showSwitchableElements(['title', 'links']);
            containerWidth = this._getContainerWidth(box);

            if (containerWidth < this.iconWidth * 2) {
              // hiden subtitle links
              this._showSwitchableElements(['title']);
              containerWidth = this._getContainerWidth(box);

              if (containerWidth < this.iconWidth * 2) {
                //hiden the title, subtitle, links
                this._showSwitchableElements([]);
                containerWidth = this._getContainerWidth(box);

                if (containerWidth < this.iconWidth * 2) {
                  //all of the elements is hidden,
                  // but the it's still can hold two icons(too small screen???),
                  //use the empty space
                  //the emptyWidth may be negative

                  emptyWidth = emptyWidth - (this.iconWidth * 2 - containerWidth);
                  containerWidth = this.iconWidth * 2;
                  this._getContainerWidth(box);
                }
              }
            }
          } else {
            //hiden the title, subtitle, links
            this._showSwitchableElements([]);
            containerWidth = this._getContainerWidth(box);
            if (containerWidth < this.iconWidth * 2) {
              //all of the elements is hidden,
              // but the it's still can hold two icons(too small screen???),
              //use the empty space
              //the emptyWidth may be negative
              emptyWidth = emptyWidth - (this.iconWidth * 2 - containerWidth);
              containerWidth = this.iconWidth * 2;
            }
          }
        }
        // console.log('containerWidth: %f, emptyWidth: %f', containerWidth, emptyWidth);
        return {
          containerWidth: containerWidth,
          emptyWidth: emptyWidth
        };
      },

      _getEmptyWidth: function(box) {
        return 1 / 10 * box.w;
      },

      _createIconNodes: function(box) {
        var iconTabIndex = this.tabIndex + 20;
        query('.icon-node', this.containerNode).remove();
        // this._closeDropMenu();

        var i, iconConfig, allIconConfigs = this.getAllConfigs();
        //by default, the icon is square
        this.iconWidth = box.h;

        var ret = this._calcContainerAndEmptyWidth(box);

        var marginProperty = window.isRTL ? 'marginRight' : 'marginLeft';
        var containerStyle = {
          width: ret.containerWidth + 'px'
        };
        //add some margein to avoid mess layout
        containerStyle[marginProperty] = (ret.emptyWidth - 20) + 'px';
        // html.setStyle(this.containerNode, containerStyle);

        var nodes = [], node;

        this.maxIconCount = Math.floor(ret.containerWidth / this.iconWidth);
        if (this.maxIconCount >= allIconConfigs.length) {
          this.headerIconCount = allIconConfigs.length;
          this.createMoreIcon = false;
        } else {
          this.headerIconCount = this.maxIconCount - 1;
          this.createMoreIcon = true;
        }
        if (this.createMoreIcon) {
          node = this._createIconNode({
            label: this.nls.more,
            name: '__more'
          });
          nodes.push(node);
        }

        var openAtStartNode;
        for (i = this.headerIconCount - 1; i >= 0; i--) {
          iconConfig = allIconConfigs[i];
          node = this._createIconNode(iconConfig);

          if (iconConfig.openAtStart) {
            openAtStartNode = node;
          }
          nodes.push(node);
        }

        nodes.reverse().forEach(function(n){
          iconTabIndex += 200;
          html.setAttr(n, 'tabindex', iconTabIndex);
          //Inpanel widgets don't need tabindex.
          //But off panel widgets do, like search widget
          if(!n.config.inPanel){
            n.config.tabIndex = iconTabIndex;
          }
        });

        if (this.createMoreIcon) {
          //1.openAtStartNode widget in __more btn
          for (i = this.headerIconCount; i < allIconConfigs.length; i++) {
            iconConfig = allIconConfigs[i];
            if (iconConfig.openAtStart) {
              openAtStartNode = iconConfig;//finding openAtStart
            }
          }
          if (openAtStartNode && !this.openAtStartWidget && !this.morePane) {
            this._showMorePane();//create nodes
            this.morePane.openNodebyId(openAtStartNode.id);//open it
          }
        } else {
          //2.without "__more" btn
          //open the first openatstart widget
          if (openAtStartNode && !this.openAtStartWidget) {
            this.openAtStartWidget = openAtStartNode.config.id;
            this._onIconClick(openAtStartNode, true);
          }

          if (this.openedId && this.getConfigById(this.openedId) &&
            this.getConfigById(this.openedId).inPanel === false) {
            var openedIconNode = this._getIconNodeById(this.openedId);
            var openedWidget = this.widgetManager.getWidgetById(this.openedId);
            if (openedIconNode && openedWidget) {
              this._setOffPanelWidgetPosition(openedIconNode, openedWidget);
            } else {
              this.widgetManager.closeWidget(this.openedId);
              this.openedId = '';
            }
          }
        }
      },

      _createIconNode: function(iconConfig) {
        var node, iconUrl;
        if (iconConfig.name === '__more') {
          iconUrl = this.folderUrl + 'images/more_icon.png';
        } else {
          iconUrl = iconConfig.icon;
        }

        node = html.create('div', {
          'class': 'icon-node jimu-float-trailing' + ((this.openedId === iconConfig.id)? ' jimu-state-selected': ''),
          title: iconConfig.label,
          // 'aria-label': iconConfig.label,
          role: 'button',
          settingId: iconConfig.id,
          style: {
            width: this.height + 'px',
            height: this.height + 'px'
          },
          'data-widget-name': iconConfig.name
        }, this.containerNode);

        var imgNode = html.create('img', {
          src: iconUrl,
          alt: iconConfig.label,
          style: {
            marginTop: ((this.height - 24) / 2) + 'px'
          }
        }, node);

        if(window.isRTL && iconConfig.mirrorIconForRTL){
          html.addClass(imgNode, 'jimu-flipx');
        }

        if (iconConfig.name === '__more') {
          this._morePaneNode = node;
          on(node, 'click', lang.hitch(this, this._showMorePane, iconConfig));
          on(node, 'keydown', lang.hitch(this, function(evt) {
            if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
              this._showMorePane();
            }
          }));
        } else {
          on(node, 'click', lang.hitch(this, function() {
            this._onIconClick(node);
          }));

          on(node, 'keydown', lang.hitch(this, function(evt) {
            if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
              this._onIconClick(node);
            }
          }));
          //add tooltips
          utils.addTooltipByDomNode(Tooltip, node, iconConfig.label);
        }
        node.config = iconConfig;
        // if(iconConfig.defaultState){
        //   this.openedId = iconConfig.id;
        //   this._switchNodeToOpen(this.openedId);
        // }
        if (node.config.widgets && node.config.widgets.length > 1 &&
          node.config.openType === 'dropDown') {
          this._createDropTriangle(node);
        }

        //set current open node
        // if (this.openedId === iconConfig.id) {
        //   html.addClass(node, 'jimu-state-selected');
        //   if (node.config.widgets && node.config.widgets.length > 1 &&
        //     node.config.openType === 'dropDown') {
        //     this._openDropMenu(node);
        //   }
        // }
        return node;
      },

      _createDropTriangle: function(node) {
        var box = html.getMarginBox(node);
        var triangleLeft = box.l + box.w / 2 - 4;
        html.create('div', {
          'class': 'drop-triangle',
          style: {
            left: triangleLeft + 'px'
          }
        }, node);
      },

      _onIconClick: function(node, isFirstTime) {
        if (!node.config.widgets || node.config.widgets.length === 1 ||
          node.config.openType === 'openAll') {
          //widget or group with 'openAll' open type
          if (this.openedId && this.openedId === node.config.id) {
            this._switchNodeToClose(this.openedId);
            return;
          } else {
            if (this.openedId) {
              this._switchNodeToClose(this.openedId).then(lang.hitch(this, function() {
                this._closeDropMenu();
                this._switchNodeToOpen(node.config.id);
              }));
            } else {
              this._switchNodeToOpen(node.config.id);
            }
          }
        } else {
          if(this.dropMenuNode){
            this._closeDropMenu();
          }else{
            this._openDropMenu(node, isFirstTime);
          }
        }
      },

      _closeDropMenu: function() {
        if (this.dropMenuNode) {
          html.destroy(this.dropMenuNode);
          this.dropMenuNode = null;
        }
      },

      _openDropMenu: function(pnode, isFirstTime) {
        this.dropMenuNode = html.create('div', {
          'class': 'jimu-drop-menu jimu-main-background',
          title: pnode.config.label,
          style: {
            position: 'absolute',
            zIndex: '101'
          }
        });

        html.place(this.dropMenuNode, this.containerNode);

        array.forEach(pnode.config.widgets, function(widgetConfig) {
          this._createDropMenuItem(widgetConfig, pnode);
        }, this);

        this._setDropMenuPosition(pnode);

        if (this.morePane) {
          this.morePane.hide();
        }

        this._initDropMenuEvent(pnode, isFirstTime);
      },

      _initDropMenuEvent:function(groupNode, isFirstTime){
        var nodes = query('.menu-item', this.dropMenuNode);
        this.own(on(nodes, 'keydown', lang.hitch(this, function(evt){
          if(evt.keyCode === keys.TAB || evt.keyCode === keys.ESCAPE){
            evt.stopPropagation();
            evt.preventDefault();
            this._closeDropMenu();
            groupNode.focus();
          } else{
            var nextItem;
            if(evt.keyCode === keys.DOWN_ARROW){
              nextItem = evt.target.nextSibling ? evt.target.nextSibling : evt.target;
            }else if(evt.keyCode === keys.UP_ARROW){
              nextItem = evt.target.previousSibling ? evt.target.previousSibling : evt.target;
            }else if(evt.keyCode === keys.HOME){
              nextItem = nodes[0];
            }else if(evt.keyCode === keys.END){
              nextItem = nodes[nodes.length - 1];
            }
            if(nextItem){
              nextItem.focus();
            }
          }
        })));

        if(isFirstTime){
          return;
        }
        nodes[0].focus();//default
      },

      _createDropMenuItem: function(sconfig, groupNode) {
        var node = html.create('div', {
          'class': 'menu-item',
          'tabindex': '-1',
          title: sconfig.label,
          style: {
            height: this.height + 'px'
          }
        }, this.dropMenuNode);

        html.create('img', {
          'class': 'jimu-float-leading',
          src: sconfig.icon
        }, node);

        html.create('div', {
          'class': 'label jimu-float-leading',
          innerHTML: utils.sanitizeHTML(sconfig.label)
        }, node);

        this.own(on(node, 'click', lang.hitch(this, function() {
          this._dropMenuItemClick(node);
        })));
        this.own(on(node, 'keydown', lang.hitch(this, function(evt) {
          if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
            this._dropMenuItemClick(node);
          }
        })));
        node.config = sconfig;
        node.config.groupNode = groupNode;//save groupNode to widgetNode's config
        return node;
      },

      _dropMenuItemClick: function(node){
        this._closeDropMenu();
        if(this.openedId){
          this._switchNodeToClose(this.openedId).then(lang.hitch(this, function() {
            this._showIconContent(node.config);
          }));
        }else{
          this._showIconContent(node.config);
        }
      },

      _setDropMenuPosition: function(pnode) {
        var position = {};
        var menuBox = html.getMarginBox(this.dropMenuNode);

        position = this._getDropdownPosition(pnode, menuBox);
        position.zIndex = 101;
        html.setStyle(this.dropMenuNode, utils.getPositionStyle(position));
      },

      _getDropdownPosition: function(pnode, sbox) {
        var position = {},
          pbox = html.getMarginBox(pnode),
          thisBox = html.getMarginBox(this.domNode);

        position.top = this.height + 1;

        if (window.isRTL) {
          if(pbox.l + pbox.w - sbox.w < 0){
            position.right = 0;
          }else{
            position.right = pbox.l + pbox.w - sbox.w;
          }
        } else {
          if(pbox.l + sbox.w > thisBox.w){
            position.right = 0;
          }else{
            position.left = pbox.l;
          }
        }
        return position;
      },

      _switchNodeToOpen: function(id) {
        var node = this._getIconNodeById(id);
        this._showIconContent(node.config);
      },

      _switchNodeToClose: function(id) {
        query('.icon-node', this.domNode).removeClass('jimu-state-selected');
        var iconJson = this.appConfig.getConfigElementById(id);
        var def;
        if(iconJson){
          if(iconJson.inPanel === false){
            this.widgetManager.closeWidget(id);
            this.openedId = '';
            def = new Deferred();
            def.resolve();
            return def;
          }else{
            return this.panelManager.closePanel(id + '_panel');
          }
        }else{
          def = new Deferred();
          def.resolve();
          return def;
        }

      },

      _getIconNodeById: function(id, isFromMorePane) {
        var domNode = isFromMorePane ? this.morePane.domNode : this.domNode;
        var node = query('.icon-node[settingId="' + id + '"]', domNode);
        if (node.length === 0) {
          return;
        }
        return node[0];
      },

      _unSelectIcon: function(id) {
        query('.icon-node[settingId="' + id + '"]', this.domNode)
          .removeClass('jimu-state-selected');
        this.openedId = '';
      },

      _showIconContent: function(iconConfig) {
        var iconNode;
        if(iconConfig.inPanel === false){
          this.widgetManager.loadWidget(iconConfig).then(lang.hitch(this, function(widget) {
            this.openedId = iconConfig.id;
            iconNode = this._getIconNodeById(iconConfig.id);

            // triggerd when current node is not inited, eg: click map-popup's action to open a widget&node which is in collapsed header-popup.
            // init more pane, then click icon node of current widget.
            if(!iconNode){
              this.openedId = '';
              this._showMorePane();
              iconNode = this._getIconNodeById(iconConfig.id, true);
              iconNode.click();
              return;
            }

            query('.icon-node', this.domNode).removeClass('jimu-state-selected');
            html.addClass(iconNode, 'jimu-state-selected');

            //we don't call widget.startup because getWidgetMarginBox has started widget
            //widget.startup();

            html.setStyle(widget.domNode, 'zIndex', 101);

            this._setOffPanelWidgetPosition(iconNode, widget);
            this.widgetManager.openWidget(widget);
            // ST: Added to listen for out of panel widgets that can be closed
            this.own(aspect.after(widget, 'onClose', lang.hitch(this, function() {
              this._unSelectIcon(iconConfig.id);
              // console.log(widget);
              // if (this.openedId && this.openedId === iconNode.config.id) {
              this._switchNodeToClose(iconNode.config.id);
              // }
              iconNode.focus();
            })));
          }));
        }else{
          this.panelManager.showPanel(iconConfig).then(lang.hitch(this, function(panel) {
            var iconNode;
            if(!iconConfig.groupNode){
              iconNode = this._getIconNodeById(iconConfig.id);
              query('.icon-node', this.domNode).removeClass('jimu-state-selected');
              html.addClass(iconNode, 'jimu-state-selected');
            }

            this.openedId = iconConfig.id;
            this.own(aspect.after(panel, 'onClose', lang.hitch(this, function() {
              if(iconConfig.groupNode){
                iconConfig.groupNode.focus();
              }else{
                this._unSelectIcon(iconConfig.id);
                iconNode.focus();
              }
            })));

            this.own(on(panel.closeNode, 'keydown', lang.hitch(this, function(evt){
              if(evt.keyCode === keys.ESCAPE){
                var focusNode = iconConfig.groupNode ? iconConfig.groupNode : iconNode;
                focusNode.focus();
              }
            })));
          }));
        }
      },

      _setOffPanelWidgetPosition: function(iconNode, widget){
        var position = this._getDropdownPosition(iconNode,
              this.widgetManager.getWidgetMarginBox(widget));
        widget.setPosition(position, this.containerNode);
      },

      _showMorePane: function() {
        var i, iconConfig, moreItems = [],
          allIconConfigs = this.getAllConfigs();
        //#13788
        // desktop: morePane is positioned relative to map
        // mobile: morePane is positioned relative to main page
        var morePaneParentNode = window.appInfo.isRunInMobile ? jimuConfig.mainPageId : jimuConfig.mapId;

        for (i = this.headerIconCount; i < allIconConfigs.length; i++) {
          iconConfig = allIconConfigs[i];
          moreItems.push(iconConfig);
        }
        if (this.morePane) {
          this.morePane.destroy();
        }
        if (this.moreIconPaneCoverNode) {
          html.destroy(this.moreIconPaneCoverNode);
        }

        this._closeDropMenu();

        this.morePane = new PopupTileNodes({
          openedId: this.openedId,
          items: moreItems,
          popupContainer: morePaneParentNode
        });
        this._createCoverNode();
        html.place(this.morePane.domNode, morePaneParentNode);
        this.morePane.startup();

        aspect.after(this.morePane, 'onNodeClicked', lang.hitch(this, function(node) {
          this._moveConfigToHeader(node.config);
          this._createIconNodes(html.getContentBox(this.domNode));
          this._onIconClick(this._getIconNodeById(node.config.id));
        }), true);
        aspect.after(this.morePane, 'hide', lang.hitch(this, function() {
          html.destroy(this.moreIconPaneCoverNode);
          this._morePaneNode.focus();
        }), true);
      },

      _moveConfigToHeader: function(config) {
        var allIconConfigs = this.getAllConfigs();

        var tempIndex = config.index;
        config.index = allIconConfigs[this.headerIconCount - 1].index;
        allIconConfigs[this.headerIconCount - 1].index = tempIndex;
      },

      _createCoverNode: function() {
        this.moreIconPaneCoverNode = html.create('div', {
          'class': 'jimu-more-icon-cover'
        }, jimuConfig.layoutId);
      }
    });
    return clazz;
  });