var TEAM_PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$';
var ROBOT_PATTERN = '^[a-zA-Z][a-zA-Z0-9]{3,29}$';
var USER_PATTERN = '^[a-z0-9_]{4,30}$';

// Define the pages module.
quayPages = angular.module('quayPages', [], function(){});

// Define a constant for creating pages.
quayPages.constant('pages', {
  '_pages': {},

  'create': function(pageName, templateName, opt_controller, opt_flags, opt_profiles) {
    var profiles = opt_profiles || ['old-layout', 'layout'];
    for (var i = 0; i < profiles.length; ++i) {
      this._pages[profiles[i] + ':' + pageName] = {
        'name': pageName,
        'controller': opt_controller,
        'templateName': templateName,
        'flags': opt_flags || {}
      };
    }
  },

  'get': function(pageName, profiles) {
    for (var i = 0; i < profiles.length; ++i) {
      var current = profiles[i];
      var key = current.id + ':' + pageName;
      var page = this._pages[key];
      if (page) {
        return [current, page];
      }
    }

    return null;
  }
});

quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'cfp.hotkeys', 'angular-tour', 'restangular', 'angularMoment',
                    'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml', 'debounce',
                    'core-ui', 'core-config-setup', 'quayPages', 'infinite-scroll'];

if (window.__config && window.__config.MIXPANEL_KEY) {
  quayDependencies.push('angulartics');
  quayDependencies.push('angulartics.mixpanel');
}

// Define the application.
quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoadingBarProvider) {
  cfpLoadingBarProvider.includeSpinner = false;
});

// Disable tooltips on touch devices.
quayApp.config(['$tooltipProvider', function ($tooltipProvider) {
  var tooltipFactory = $tooltipProvider.$get[$tooltipProvider.$get.length - 1];

  // decorate the tooltip getter
  $tooltipProvider.$get[$tooltipProvider.$get.length - 1] = function($window) {
    if ('ontouchstart' in $window) {
      var existing = tooltipFactory.apply(this, arguments);
      return function(element) {
        // Note: We only disable bs-tooltip's themselves. $tooltip is used for other things
        // (such as the datepicker), so we need to be specific when canceling it.
        if (element.attr('bs-tooltip') == null) {
          return existing.apply(this, arguments);
        } else {
          return null;
        }
      };
    }

    return tooltipFactory.apply(this, arguments);
  };
}]);

// Configure the routes.
quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeProvider, $locationProvider, pages) {
  var title = window.__config['REGISTRY_TITLE'] || 'Quay.io';

  $locationProvider.html5Mode(true);

  // WARNING WARNING WARNING
  // If you add a route here, you must add a corresponding route in thr endpoints/web.py
  // index rule to make sure that deep links directly deep into the app continue to work.
  // WARNING WARNING WARNING

  var layoutProfile = 'layout';
  window.console.log('Using layout profile: ' + layoutProfile);

  var routeBuilder = new AngularRouteBuilder($routeProvider, pages, [
    // Start with the old pages (if we asked for it).
    {id: 'old-layout', templatePath: '/static/partials/'},

    // Fallback back combined new/existing pages.
    {id: 'layout', templatePath: '/static/partials/'}
  ], layoutProfile);

  routeBuilder
    // Repository View
    .route('/repository/:namespace/:name', 'repo-view')
    .route('/repository/:namespace/:name/tag/:tag', 'repo-view')

    // Image View
    .route('/repository/:namespace/:name/image/:image', 'image-view')

    // Repo Build View
    .route('/repository/:namespace/:name/build/:buildid', 'build-view')

    // Repo List
    .route('/repository/', 'repo-list')

    // Organizations
    .route('/organizations/', 'organizations')

    // New Organization
    .route('/organizations/new/', 'new-organization')

    // View Organization
    .route('/organization/:orgname', 'org-view')

    // View Organization Team
    .route('/organization/:orgname/teams/:teamname', 'team-view')

    // Organization View Application
    .route('/organization/:orgname/application/:clientid', 'manage-application')

    // View User
    .route('/user/:username', 'user-view')

    // Sign In
    .route('/signin/', 'signin')

    // New Repository
    .route('/new/', 'new-repo')

    // ER Management
    .route('/superuser/', 'superuser')

    // ER Setup
    .route('/setup/', 'setup')

    // Plans
    .route('/plans/', 'plans')

    // Tutorial
    .route('/tutorial/', 'tutorial')

    // Contact
    .route('/contact/', 'contact')

    // About
    .route('/about/', 'about')

    // Security
    .route('/security/', 'security')

    // Landing Page
    .route('/', 'landing')

    // Tour
    .route('/tour/', 'tour')
    .route('/tour/features', 'tour')
    .route('/tour/organizations', 'tour')
    .route('/tour/enterprise', 'tour')

    // Confirm Invite
    .route('/confirminvite', 'confirm-invite')

    // Default: Redirect to the landing page
    .otherwise({redirectTo: '/'});
}]);

// Configure compile provider to add additional URL prefixes to the sanitization list. We use
// these on the Contact page.
quayApp.config(function($compileProvider) {
  $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|irc):/);
});

// Configure the API provider.
quayApp.config(function(RestangularProvider) {
  RestangularProvider.setBaseUrl('/api/v1/');
});

// Configure analytics.
if (window.__config && window.__config.MIXPANEL_KEY) {
  quayApp.config(['$analyticsProvider', function($analyticsProvider) {
    $analyticsProvider.virtualPageviews(true);
  }]);
}

// Configure sentry.
if (window.__config && window.__config.SENTRY_PUBLIC_DSN) {
  quayApp.config(function($provide) {
    $provide.decorator("$exceptionHandler", function($delegate) {
      return function(ex, cause) {
        $delegate(ex, cause);
        Raven.captureException(ex, {extra: {cause: cause}});
      };
    });
  });
}

// Run the application.
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService', 'MetaService', 'UIService',
  function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService, MetaService, UIService) {

  var defaultTitle = window.__config['REGISTRY_TITLE'] || 'Quay.io';

  // Handle session security.
  Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});

  // Handle session expiration.
  Restangular.setErrorInterceptor(function(response) {
    if (response.status == 401 && response.data['error_type'] == 'invalid_token' &&
        response.data['session_required'] !== false) {
      $('#sessionexpiredModal').modal({});
      return false;
    }

    if (response.status == 503) {
      $('#cannotContactService').modal({});
      return false;
    }

    if (response.status == 500) {
      document.location = '/500';
      return false;
    }

    return true;
  });

  // Check if we need to redirect based on a previously chosen plan.
  var result = PlanService.handleNotedPlan();

  // Check to see if we need to show a redirection page.
  var redirectUrl = CookieService.get('quay.redirectAfterLoad');
  CookieService.clear('quay.redirectAfterLoad');

  if (!result && redirectUrl && redirectUrl.indexOf(window.location.href) == 0) {
    window.location = redirectUrl;
    return;
  }

  $rootScope.$watch('description', function(description) {
    if (!description) {
      description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
    }

    // Note: We set the content of the description tag manually here rather than using Angular binding
    // because we need the <meta> tag to have a default description that is not of the form "{{ description }}",
    // we read by tools that do not properly invoke the Angular code.
    $('#descriptionTag').attr('content', description);
  });

  // Listen for scope changes and update the title and description accordingly.
  $rootScope.$watch(function() {
    var title = MetaService.getTitle($rootScope.currentPage) || defaultTitle;
    if ($rootScope.title != title) {
      $rootScope.title = title;
    }

    var description = MetaService.getDescription($rootScope.currentPage) || '';
    if ($rootScope.description != description) {
      $rootScope.description = description;
    }
  });

  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
    $rootScope.current = current.$$route;
    $rootScope.currentPage = current;

    $rootScope.pageClass = '';

    if (!current.$$route) { return; }

    $rootScope.pageClass = current.$$route.pageClass || '';
    $rootScope.newLayout = !!current.$$route.newLayout;
    $rootScope.fixFooter = !!current.$$route.fixFooter;

    $anchorScroll();
  });

  var initallyChecked = false;
  window.__isLoading = function() {
    if (!initallyChecked) {
      initallyChecked = true;
      return true;
    }
    return $http.pendingRequests.length > 0;
  };
}]);
