text: angular.module('platformWebApp')
      .factory('platformWebApp.toolbarService', function () {
          var toolbarCommandsMap = [];
          return {
              register: function (toolbarItem, toolbarController) {
                  var map = toolbarCommandsMap;
                  if (!map[toolbarController]) {
                      map[toolbarController] = [];
                  }
  
                  map[toolbarController].push(toolbarItem);
                  map[toolbarController].sort(function (a, b) {
                      return a.index - b.index;
                  });
              },
              tryRegister: function (toolbarItem, toolbarController) {
                  var map = toolbarCommandsMap;
                  if (!map[toolbarController] || !_.findWhere(map[toolbarController], { name: toolbarItem.name })) {
                      this.register(toolbarItem, toolbarController);
                  }
              },
              resolve: function (bladeCommands, toolbarController) {
                  var externalCommands = toolbarCommandsMap[toolbarController];
                  if (externalCommands) {
                      bladeCommands = angular.copy(bladeCommands || []);
  
                      _.each(externalCommands, function (newCommand) {
                          var overrideIndex = _.findIndex(bladeCommands, function (bladeCommand) {
                              return bladeCommand.name === newCommand.name;
                          });
                          var deleteCount = overrideIndex >= 0 ? 1 : 0;
                          overrideIndex = overrideIndex >= 0 ? overrideIndex : newCommand.index;
  
                          bladeCommands.splice(overrideIndex, deleteCount, newCommand);
                      });
                  }
  
                  return bladeCommands;
              }
          };
      })
      .directive('vaBladeContainer', ['platformWebApp.bladeNavigationService', function (bladeNavigationService) {
          return {
              restrict: 'E',
              replace: true,
              templateUrl: '$(Platform)/Scripts/app/navigation/blade/bladeContainer.tpl.html',
              link: function (scope) {
                  scope.blades = bladeNavigationService.stateBlades();
              }
          }
      }])
      .directive('vaBlade', ['$compile', 'platformWebApp.bladeNavigationService', 'platformWebApp.toolbarService', '$timeout', '$document', 'platformWebApp.dialogService', function ($compile, bladeNavigationService, toolbarService, $timeout, $document, dialogService) {
          return {
              terminal: true,
              priority: 100,
              link: function (scope, element) {
                  element.attr('ng-controller', scope.blade.controller);
                  element.attr('id', scope.blade.id);
                  element.attr('ng-model', "blade");
                  element.removeAttr("va-blade");
                  $compile(element)(scope);
                  scope.blade.$scope = scope;
  
                  var mainContent = $('.cnt');
                  var currentBlade = $(element).parent();
                  var parentBlade = currentBlade.prev();
  
                  if (!scope.blade.disableOpenAnimation) {
                      scope.blade.animated = true;
                      $timeout(function () {
                          scope.blade.animated = false;
                      }, 250);
                  }
  
                  function scrollContent(scrollToBlade, scrollToElement) {
                      if (!scrollToBlade) {
                          scrollToBlade = scope.blade;
                      }
                      if (!scrollToElement) {
                          scrollToElement = currentBlade;
                      }
  
                      // we can't just get current blade position (because of animation) or calculate it
                      // via parent position + parent width (because we may open parent and child blade at the same time)
                      // instead, we need to use sum of width of all blades
                      var previousBlades = scrollToElement.prevAll();
                      var previousBladesWidthSum = 0;
                      previousBlades.each(function () {
                          previousBladesWidthSum += $(this).outerWidth();
                      });
                      var scrollLeft = previousBladesWidthSum + scrollToElement.outerWidth(!(scrollToBlade.isExpanded || scrollToBlade.isMaximized)) - mainContent.width();
                      mainContent.animate({ scrollLeft: (scrollLeft > 0 ? scrollLeft : 0) }, 500);
                  }
  
                  var updateSize = function () {
                      var contentBlock = currentBlade.find(".blade-content");
                      var containerBlock = currentBlade.find(".blade-container");
  
                      var bladeWidth = "";
                      var bladeMinWidth = "";
  
                      if ((scope.blade.isExpandable && scope.blade.isExpanded) || (!scope.blade.isExpandable && scope.blade.isMaximized)) {
                          // minimal required width + container padding
                          bladeMinWidth = 'calc(' + contentBlock.css("min-width") + ' + ' + parseInt(containerBlock.outerWidth() - containerBlock.width()) + 'px)';
                      }
  
                      if (scope.blade.isExpandable && scope.blade.isExpanded) {
                          var offset = parentBlade.length > 0 ? parentBlade.width() : 0;
                          // free space of view - parent blade size (if exist)
                          bladeWidth = 'calc(100% - ' + offset + 'px)';
                      } else if (!scope.blade.isExpandable && scope.blade.isMaximized) {
                          currentBlade.attr('data-width', currentBlade.outerWidth());
                          bladeWidth = '100%';
                      }
  
                      currentBlade.width(bladeWidth);
                      currentBlade.css('min-width', bladeMinWidth);
  
                      setVisibleToolsLimit();
                  }
  
                  scope.$watch('blade.isExpanded', function () {
                      // we must recalculate position only at next digest cycle,
                      // because at this time blade UI is not fully (re)initialized
                      // for example, ng-class set classes after this watch called
                      $timeout(updateSize, 0, false);
                  });
  
                  scope.$on('$includeContentLoaded', function (event, src) {
                      if (src === scope.blade.template) {
                          // see above
                          $timeout(function () {
                              updateSize();
                              scrollContent();
                          }, 0, false);
                      }
                  });
  
                  scope.bladeMaximize = function () {
                      scope.blade.isMaximized = true;
                      updateSize();
                      scrollContent();
                  };
  
                  scope.bladeMinimize = function () {
                      scope.blade.isMaximized = false;
                      updateSize();
                  };
  
                  scope.bladeClose = function (onAfterClose) {
                      bladeNavigationService.closeBlade(scope.blade, onAfterClose, function (callback) {
                          scope.blade.animated = true;
                          scope.blade.closing = true;
                          $timeout(function () {
                              currentBlade.remove();
                              scrollContent(scope.blade.parentBlade, parentBlade);
                              callback();
                          }, 125, false);
                      });
                  };
  
                  scope.$watch('blade.toolbarCommands', function (toolbarCommands) {
                      scope.resolvedToolbarCommands = toolbarService.resolve(toolbarCommands, scope.blade.controller);
  
                      setVisibleToolsLimit();
                  }, true);
  
                  var toolbar = currentBlade.find(".blade-toolbar .menu.__inline");
  
                  function setVisibleToolsLimit() {
                      scope.toolsPerLineCount = scope.resolvedToolbarCommands ? scope.resolvedToolbarCommands.length : 1;
  
                      $timeout(function () {
                          if (toolbar.height() > 55 && scope.toolsPerLineCount > 1) {
                              var maxToolbarWidth = toolbar.width() - 46; // the 'more' button is 46px wide
                              //console.log(toolbar.width() + 'maxToolbarWidth: ' + maxToolbarWidth);
                              var toolsWidth = 0;
                              var lis = toolbar.find("li");
                              var i = 0;
                              while (maxToolbarWidth > toolsWidth && lis.length > i) {
                                  toolsWidth += lis[i].clientWidth;
                                  i++;
                              }
                              scope.toolsPerLineCount = i - 1;
                          }
                      }, 220);
                  }
  
                  function handleClickEvent() {
                      setVisibleToolsLimit();
                      $document.unbind('click', handleClickEvent);
                  }
  
                  scope.showMoreTools = function (event) {
                      scope.toolsPerLineCount = scope.resolvedToolbarCommands.length;
  
                      event.stopPropagation();
                      $document.bind('click', handleClickEvent);
                  };
  
                  scope.showErrorDetails = function () {
                      var dialog = { id: "errorDetails" };
                      if (scope.blade.errorBody != undefined)
                          dialog.message = scope.blade.errorBody;
                      dialogService.showDialog(dialog, '$(Platform)/Scripts/app/modularity/dialogs/errorDetails-dialog.tpl.html', 'platformWebApp.confirmDialogController');
                  };
              }
          }
      }])
      .factory('platformWebApp.bladeNavigationService', ['platformWebApp.authService', '$timeout', '$state', 'platformWebApp.dialogService', function (authService, $timeout, $state, dialogService) {
  
          function showConfirmationIfNeeded(showConfirmation, canSave, blade, saveChangesCallback, closeCallback, saveTitle, saveMessage) {
              if (showConfirmation) {
                  var dialog = { id: "confirmCurrentBladeClose" };
  
                  if (canSave) {
                      dialog.title = saveTitle;
                      dialog.message = saveMessage;
                  } else {
                      dialog.title = "Warning";
                      dialog.message = "Validation failed for this object. Will you continue editing and save later?";
                  }
  
                  dialog.callback = function (userChoseYes) {
                      if (canSave) {
                          if (userChoseYes) {
                              saveChangesCallback();
                          }
                          closeCallback();
                      } else if (!userChoseYes) {
                          closeCallback();
                      }
                  };
  
                  dialogService.showConfirmationDialog(dialog);
              }
              else {
                  closeCallback();
              }
          }
  
          var service = {
              blades: [],
              currentBlade: undefined,
              showConfirmationIfNeeded: showConfirmationIfNeeded,
              closeBlade: function (blade, callback, onBeforeClosing) {
                  //Need in case a copy was passed
                  blade = service.findBlade(blade.id) || blade;
  
                  // close all children
                  service.closeChildrenBlades(blade, function () {
                      var doCloseBlade = function () {
                          if (angular.isFunction(onBeforeClosing)) {
                              onBeforeClosing(doCloseBladeFinal);
                          } else {
                              doCloseBladeFinal();
                          }
                      }
  
                      var doCloseBladeFinal = function () {
                          var idx = service.stateBlades().indexOf(blade);
                          if (idx >= 0) service.stateBlades().splice(idx, 1);
  
                          //remove blade from children collection
                          if (angular.isDefined(blade.parentBlade)) {
                              var childIdx = blade.parentBlade.childrenBlades.indexOf(blade);
                              if (childIdx >= 0) {
                                  blade.parentBlade.childrenBlades.splice(childIdx, 1);
                              }
                          }
                          if (angular.isFunction(callback)) {
                              callback();
                          };
                      };
  
                      if (angular.isFunction(blade.onClose)) {
                          blade.onClose(doCloseBlade);
                      }
                      else {
                          doCloseBlade();
                      }
                  });
  
                  if (blade.parentBlade && blade.parentBlade.isExpandable) {
                      blade.parentBlade.isExpanded = true;
                      if (angular.isFunction(blade.parentBlade.onExpand)) {
                          blade.parentBlade.onExpand();
                      }
                  }
              },
              closeChildrenBlades: function (blade, callback) {
                  if (blade && _.any(blade.childrenBlades)) {
                      angular.forEach(blade.childrenBlades.slice(), function (child) {
                          service.closeBlade(child, function () {
                              // show only when all children were closed
                              if (blade.childrenBlades.length == 0 && angular.isFunction(callback)) {
                                  callback();
                              }
                          });
                      });
                  } else if (angular.isFunction(callback)) {
                      callback();
                  }
              },
              stateBlades: function (stateName) {
                  if (angular.isUndefined(stateName)) {
                      stateName = $state.current.name;
                  }
  
                  if (angular.isUndefined(service.blades[stateName])) {
                      service.blades[stateName] = [];
                  }
  
                  return service.blades[stateName];
              },
              findBlade: function (id) {
                  var found;
                  angular.forEach(service.stateBlades(), function (blade) {
                      if (blade.id == id) {
                          found = blade;
                      }
                  });
  
                  return found;
              },
              showBlade: function (blade, parentBlade) {
  
                  //If it is first blade for state try to open saved blades
                  //var firstStateBlade = service.stateBlades($state.current.name)[0];
                  //if (angular.isDefined(firstStateBlade) && firstStateBlade.id == blade.id) {
                  //    service.currentBlade = firstStateBlade;
                  //    return;
                  //}
                  blade.errorBody = "";
                  blade.isLoading = true;
                  blade.parentBlade = parentBlade;
                  blade.childrenBlades = [];
                  if (parentBlade) {
                      blade.headIcon = blade.headIcon || parentBlade.headIcon;
                      blade.updatePermission = blade.updatePermission || parentBlade.updatePermission;
                  }
                  //copy securityscopes from parent blade
                  if (parentBlade != null && parentBlade.securityScopes) {
                      //need merge scopes
                      if (angular.isArray(blade.securityScopes) && angular.isArray(parentBlade.securityScopes)) {
                          blade.securityScopes = parentBlade.securityScopes.concat(blade.securityScopes);
                      }
                      else {
                          blade.securityScopes = parentBlade.securityScopes;
                      }
                  }
  
                  var existingBlade = service.findBlade(blade.id);
  
                  //Show blade in previous location
                  if (existingBlade != undefined) {
                      //store prev blade x-index
                      blade.xindex = existingBlade.xindex;
                  } else if (!angular.isDefined(blade.xindex)) {
                      //Show blade as last one by default
                      blade.xindex = service.stateBlades().length;
                  }
  
                  var showBlade = function () {
                      if (angular.isDefined(parentBlade)) {
                          blade.xindex = service.stateBlades().indexOf(parentBlade) + 1;
                          parentBlade.childrenBlades.push(blade);
                      }
                      //show blade in same place where it was
                      service.stateBlades().splice(Math.min(blade.xindex, service.stateBlades().length), 0, blade);
                      service.currentBlade = blade;
                  };
  
                  if (angular.isDefined(parentBlade) && parentBlade.childrenBlades.length > 0) {
                      service.closeChildrenBlades(parentBlade, showBlade);
                  }
                  else if (angular.isDefined(existingBlade)) {
                      service.closeBlade(existingBlade, showBlade);
                  }
                  else {
                      $timeout(function () {
                          showBlade();
                      });
                  }
  
                  if (parentBlade && parentBlade.isExpandable && parentBlade.isExpanded) {
                      parentBlade.isExpanded = false;
                      if (angular.isFunction(parentBlade.onCollapse)) {
                          parentBlade.onCollapse();
                      }
                  }
  
                  if (blade.isExpandable) {
                      blade.isExpanded = true;
                      if (angular.isFunction(blade.onExpand)) {
                          blade.onExpand();
                      }
                  }
  
                  blade.hasUpdatePermission = function () {
                      return authService.checkPermission(blade.updatePermission, blade.securityScopes);
                  };
              },
              checkPermission: authService.checkPermission,
              setError: function (response, blade) {
                  if (blade && response) {
                      blade.isLoading = false;
                      blade.error = response.status && response.statusText ? response.status + ': ' + response.statusText : response;
                      blade.errorBody = response.data ? response.data.exceptionMessage || response.data.message || response.data.errors.join('<br>') : blade.errorBody || blade.error;
                  }
              }
          };
  
          return service;
      }]);
  