AtajoUiModule

.controller('$atajoUiHeaderBar', [
  '$scope',
  '$element',
  '$attrs',
  '$q',
  '$atajoUiConfig',
  '$atajoUiHistory',
function($scope, $element, $attrs, $q, $atajoUiConfig, $atajoUiHistory) {
  var TITLE = 'title';
  var BACK_TEXT = 'back-text';
  var BACK_BUTTON = 'back-button';
  var DEFAULT_TITLE = 'default-title';
  var PREVIOUS_TITLE = 'previous-title';
  var HIDE = 'hide';

  var self = this;
  var titleText = '';
  var previousTitleText = '';
  var titleLeft = 0;
  var titleRight = 0;
  var titleCss = '';
  var isBackEnabled = false;
  var isBackShown = true;
  var isNavBackShown = true;
  var isBackElementShown = false;
  var titleTextWidth = 0;


  self.beforeEnter = function(viewData) {
    $scope.$broadcast('$atajoUiView.beforeEnter', viewData);
  };


  self.title = function(newTitleText) {
    if (arguments.length && newTitleText !== titleText) {
      getEle(TITLE).innerHTML = newTitleText;
      titleText = newTitleText;
      titleTextWidth = 0;
    }
    return titleText;
  };


  self.enableBack = function(shouldEnable, disableReset) {
    // whether or not the back button show be visible, according
    // to the navigation and history
    if (arguments.length) {
      isBackEnabled = shouldEnable;
      if (!disableReset) self.updateBackButton();
    }
    return isBackEnabled;
  };


  self.showBack = function(shouldShow, disableReset) {
    // different from enableBack() because this will always have the back
    // visually hidden if false, even if the history says it should show
    if (arguments.length) {
      isBackShown = shouldShow;
      if (!disableReset) self.updateBackButton();
    }
    return isBackShown;
  };


  self.showNavBack = function(shouldShow) {
    // different from showBack() because this is for the entire nav bar's
    // setting for all of it's child headers. For internal use.
    isNavBackShown = shouldShow;
    self.updateBackButton();
  };


  self.updateBackButton = function() {
    var ele;
    if ((isBackShown && isNavBackShown && isBackEnabled) !== isBackElementShown) {
      isBackElementShown = isBackShown && isNavBackShown && isBackEnabled;
      ele = getEle(BACK_BUTTON);
      ele && ele.classList[ isBackElementShown ? 'remove' : 'add' ](HIDE);
    }

    if (isBackEnabled) {
      ele = ele || getEle(BACK_BUTTON);
      if (ele) {
        if (self.backButtonIcon !== $atajoUiConfig.backButton.icon()) {
          ele = getEle(BACK_BUTTON + ' .icon');
          if (ele) {
            self.backButtonIcon = $atajoUiConfig.backButton.icon();
            ele.className = 'icon ' + self.backButtonIcon;
          }
        }

        if (self.backButtonText !== $atajoUiConfig.backButton.text()) {
          ele = getEle(BACK_BUTTON + ' .back-text');
          if (ele) {
            ele.textContent = self.backButtonText = $atajoUiConfig.backButton.text();
          }
        }
      }
    }
  };


  self.titleTextWidth = function() {
    var element = getEle(TITLE);
    if ( element ) {
      // If the element has a nav-bar-title, use that instead
      // to calculate the width of the title
      var children = angular.element(element).children();
      for ( var i = 0; i < children.length; i++ ) {
        if ( angular.element(children[i]).hasClass('nav-bar-title') ) {
          element = children[i];
          break;
        }
      }
    }
    var bounds = atajoui.DomUtil.getTextBounds(element);
    titleTextWidth = Math.min(bounds && bounds.width || 30);
    return titleTextWidth;
  };


  self.titleWidth = function() {
    var titleWidth = self.titleTextWidth();
    var offsetWidth = getEle(TITLE).offsetWidth;
    if (offsetWidth < titleWidth) {
      titleWidth = offsetWidth + (titleLeft - titleRight - 5);
    }
    return titleWidth;
  };


  self.titleTextX = function() {
    return ($element[0].offsetWidth / 2) - (self.titleWidth() / 2);
  };


  self.titleLeftRight = function() {
    return titleLeft - titleRight;
  };


  self.backButtonTextLeft = function() {
    var offsetLeft = 0;
    var ele = getEle(BACK_TEXT);
    while (ele) {
      offsetLeft += ele.offsetLeft;
      ele = ele.parentElement;
    }
    return offsetLeft;
  };


  self.resetBackButton = function(viewData) {
    if ($atajoUiConfig.backButton.previousTitleText()) {
      var previousTitleEle = getEle(PREVIOUS_TITLE);
      if (previousTitleEle) {
        previousTitleEle.classList.remove(HIDE);

        var view = (viewData && $atajoUiHistory.getViewById(viewData.viewId));
        var newPreviousTitleText = $atajoUiHistory.backTitle(view);

        if (newPreviousTitleText !== previousTitleText) {
          previousTitleText = previousTitleEle.innerHTML = newPreviousTitleText;
        }
      }
      var defaultTitleEle = getEle(DEFAULT_TITLE);
      if (defaultTitleEle) {
        defaultTitleEle.classList.remove(HIDE);
      }
    }
  };


  self.align = function(textAlign) {
    var titleEle = getEle(TITLE);

    textAlign = textAlign || $attrs.alignTitle || $atajoUiConfig.navBar.alignTitle();

    var widths = self.calcWidths(textAlign, false);

    if (isBackShown && previousTitleText && $atajoUiConfig.backButton.previousTitleText()) {
      var previousTitleWidths = self.calcWidths(textAlign, true);

      var availableTitleWidth = $element[0].offsetWidth - previousTitleWidths.titleLeft - previousTitleWidths.titleRight;

      if (self.titleTextWidth() <= availableTitleWidth) {
        widths = previousTitleWidths;
      }
    }

    return self.updatePositions(titleEle, widths.titleLeft, widths.titleRight, widths.buttonsLeft, widths.buttonsRight, widths.css, widths.showPrevTitle);
  };


  self.calcWidths = function(textAlign, isPreviousTitle) {
    var titleEle = getEle(TITLE);
    var backBtnEle = getEle(BACK_BUTTON);
    var x, y, z, b, c, d, childSize, bounds;
    var childNodes = $element[0].childNodes;
    var buttonsLeft = 0;
    var buttonsRight = 0;
    var isCountRightOfTitle;
    var updateTitleLeft = 0;
    var updateTitleRight = 0;
    var updateCss = '';
    var backButtonWidth = 0;

    // Compute how wide the left children are
    // Skip all titles (there may still be two titles, one leaving the dom)
    // Once we encounter a titleEle, realize we are now counting the right-buttons, not left
    for (x = 0; x < childNodes.length; x++) {
      c = childNodes[x];

      childSize = 0;
      if (c.nodeType == 1) {
        // element node
        if (c === titleEle) {
          isCountRightOfTitle = true;
          continue;
        }

        if (c.classList.contains(HIDE)) {
          continue;
        }

        if (isBackShown && c === backBtnEle) {

          for (y = 0; y < c.childNodes.length; y++) {
            b = c.childNodes[y];

            if (b.nodeType == 1) {

              if (b.classList.contains(BACK_TEXT)) {
                for (z = 0; z < b.children.length; z++) {
                  d = b.children[z];

                  if (isPreviousTitle) {
                    if (d.classList.contains(DEFAULT_TITLE)) continue;
                    backButtonWidth += d.offsetWidth;
                  } else {
                    if (d.classList.contains(PREVIOUS_TITLE)) continue;
                    backButtonWidth += d.offsetWidth;
                  }
                }

              } else {
                backButtonWidth += b.offsetWidth;
              }

            } else if (b.nodeType == 3 && b.nodeValue.trim()) {
              bounds = atajoui.DomUtil.getTextBounds(b);
              backButtonWidth += bounds && bounds.width || 0;
            }

          }
          childSize = backButtonWidth || c.offsetWidth;

        } else {
          // not the title, not the back button, not a hidden element
          childSize = c.offsetWidth;
        }

      } else if (c.nodeType == 3 && c.nodeValue.trim()) {
        // text node
        bounds = atajoui.DomUtil.getTextBounds(c);
        childSize = bounds && bounds.width || 0;
      }

      if (isCountRightOfTitle) {
        buttonsRight += childSize;
      } else {
        buttonsLeft += childSize;
      }
    }

    // Size and align the header titleEle based on the sizes of the left and
    // right children, and the desired alignment mode
    if (textAlign == 'left') {
      updateCss = 'title-left';
      if (buttonsLeft) {
        updateTitleLeft = buttonsLeft + 15;
      }
      if (buttonsRight) {
        updateTitleRight = buttonsRight + 15;
      }

    } else if (textAlign == 'right') {
      updateCss = 'title-right';
      if (buttonsLeft) {
        updateTitleLeft = buttonsLeft + 15;
      }
      if (buttonsRight) {
        updateTitleRight = buttonsRight + 15;
      }

    } else {
      // center the default
      var margin = Math.max(buttonsLeft, buttonsRight) + 10;
      if (margin > 10) {
        updateTitleLeft = updateTitleRight = margin;
      }
    }

    return {
      backButtonWidth: backButtonWidth,
      buttonsLeft: buttonsLeft,
      buttonsRight: buttonsRight,
      titleLeft: updateTitleLeft,
      titleRight: updateTitleRight,
      showPrevTitle: isPreviousTitle,
      css: updateCss
    };
  };


  self.updatePositions = function(titleEle, updateTitleLeft, updateTitleRight, buttonsLeft, buttonsRight, updateCss, showPreviousTitle) {
    var deferred = $q.defer();

    // only make DOM updates when there are actual changes
    if (titleEle) {
      if (updateTitleLeft !== titleLeft) {
        titleEle.style.left = updateTitleLeft ? updateTitleLeft + 'px' : '';
        titleLeft = updateTitleLeft;
      }
      if (updateTitleRight !== titleRight) {
        titleEle.style.right = updateTitleRight ? updateTitleRight + 'px' : '';
        titleRight = updateTitleRight;
      }

      if (updateCss !== titleCss) {
        updateCss && titleEle.classList.add(updateCss);
        titleCss && titleEle.classList.remove(titleCss);
        titleCss = updateCss;
      }
    }

    if ($atajoUiConfig.backButton.previousTitleText()) {
      var prevTitle = getEle(PREVIOUS_TITLE);
      var defaultTitle = getEle(DEFAULT_TITLE);

      prevTitle && prevTitle.classList[ showPreviousTitle ? 'remove' : 'add'](HIDE);
      defaultTitle && defaultTitle.classList[ showPreviousTitle ? 'add' : 'remove'](HIDE);
    }

    atajoui.requestAnimationFrame(function() {
      if (titleEle && titleEle.offsetWidth + 10 < titleEle.scrollWidth) {
        var minRight = buttonsRight + 5;
        var testRight = $element[0].offsetWidth - titleLeft - self.titleTextWidth() - 20;
        updateTitleRight = testRight < minRight ? minRight : testRight;
        if (updateTitleRight !== titleRight) {
          titleEle.style.right = updateTitleRight + 'px';
          titleRight = updateTitleRight;
        }
      }
      deferred.resolve();
    });

    return deferred.promise;
  };


  self.setCss = function(elementClassname, css) {
    atajoui.DomUtil.cachedStyles(getEle(elementClassname), css);
  };


  var eleCache = {};
  function getEle(className) {
    if (!eleCache[className]) {
      eleCache[className] = $element[0].querySelector('.' + className);
    }
    return eleCache[className];
  }


  $scope.$on('$destroy', function() {
    for (var n in eleCache) eleCache[n] = null;
  });

}]);