(function () {
  'use strict';

  window.ngOauth3App = angular.module('oauth3Playground', [ 'oauth3.org' ])
  //window.ngOauth3App = angular.module('oauth3Playground', [ 'ui.router' ])
/*
	ngOauth3App.config(function($stateProvider) {
		var helloState = {
			name: 'hello',
			url: '/hello',
			template: '<h3>hello world!</h3>'
		}

		var aboutState = {
			name: 'about',
			url: '/about',
			template: '<h3>Its the UI-Router hello world app!</h3>'
		}

		$stateProvider.state(helloState);
		$stateProvider.state(aboutState);
	});
*/
	.controller('PlaygroundCtrl', [ '$timeout', 'azp@oauth3.org', function ($timeout, OAUTH3) {
    // NOTE: This OAUTH3 is the same as window.OAUTH3, but with angular's promise injected
    // TODO: how to load more than one version of oauth3 on the page (i.e. a vanilla version without angular entaglement)
		var vm = this;

    vm.clientUri = OAUTH3.clientUri(window.location);
    vm.conf = { client_id: vm.clientUri, client_uri: vm.clientUri, provider_uri: vm.clientUri };
    vm.providerUri = vm.conf.client_uri;
    // map of things being debounced presently
    vm.debouncing = {};

    vm.form = {};
    vm.form.id = '';
    vm.form.user = '';
    vm.form.userProvider = '';
    vm.form.provider = '';

    vm.locks = {};
    vm.validated = {};

    //
    // Convenience for our app
    //
    vm.fn = {};
    vm.fn._debounce = {};
    vm.fn.debounceUi = function () {
      if (vm.debouncing.user || vm.debouncing.provider) {
        vm.locks['login'] = true;
      } else {
        vm.locks['login'] = false;
      }
    };
    vm.fn.debounce = function (name, time) {
      vm.debouncing[name] = true;
      vm.fn.debounceUi();
      $timeout.cancel(vm.fn._debounce[name]);
      vm.fn._debounce[name] = $timeout(function () {
        vm.debouncing[name] = false;
        vm.fn.debounceUi();
        // do nothing, just use promise
        return;
      }, time || 250);
      return vm.fn._debounce[name];
    }
    vm.fn.changeUser = function () {
      var parts = vm.form.id.split('@');
      var user;
      var provider;

      if (/@/.test(vm.form.id)) {
        // The username may have a single @, the provider may not
        // user@thing.com@whatever.com -> user@thing.com, whatever.com
        provider = parts.pop();
        vm.form.user = parts.join('');
      } else {
        //vm.form.hasUser = false;
        vm.form.user = '';
        provider = parts.join('');
      }

      if (!vm.form.providerIndependent) {
        vm.form.provider = provider;
      }
      vm.form.userProvider = provider;

      parts = vm.form.provider.split('.');
      return vm.fn.debounce('provider', 250).then(function () {
        // Careful: don't use state within a debounce function
        // uses vm.form.provider for lookup
        if (parts.length >= 2 && parts[parts.length - 1].length >= 2 && parts.every(function (p) {return p.length})) {
          return vm.api.discover();
        }
      });
    };
    vm.fn.changeProvider = function () {
      vm.form.providerIndependent = true;

      var parts = vm.form.provider.split('.');
      vm.fn.debounce('provider', 250).then(function () {
        // Careful: don't use state within a debounce function
        if (parts.length >= 2 && parts[parts.length - 1].length >= 2 && parts.every(function (p) {return p.length})) {
          return vm.api.discover();
        }
      });
    };
    vm.fn.toggleAdvanced = function () {
      vm.advanced = !vm.advanced;
      vm.form.provider = vm.form.userProvider;
      if (!vm.advanced) {
        vm.form.providerIndependent = false;
        vm.fn.changeUser();
      }
    };

    vm.fn.lock = function () {
      vm._working = true;
    };
    vm.fn.unlock = function () {
      vm._working = false;
    };
    vm.fn.clearError = function () {
      vm.error = null;
    };
    vm.fn.clearDirectives = function () {
      vm.directives = null;
    };

    //
    // Wrap around the OAUTH3 APIs
    //
    vm.api = {};
    vm.api.providerUri = function () {
      console.log('[DEBUG] providerUri:', vm.providerUri);
      try {
        vm.providerUri = OAUTH3.uri.normalize(vm.providerUri);
        vm.conf.provider_uri = vm.providerUri;
      } catch(e) {
        vm.error = e;
      }
    };
    vm.api.clientUri = function () {
      console.log('[DEBUG] clientUri:', vm.clientUri);
      try {
        vm.clientUri = OAUTH3.clientUri({ host: vm.clientUri });
        if (vm.clientUri) {
      console.log('[DEBUG] clientUri:', vm.clientUri);
          vm.conf.client_uri = vm.clientUri;
          vm.conf.client_id = vm.clientUri;
        }
      } catch(e) {
        vm.error = e;
      }
    };
    vm.api._discoverCount = 0;
    vm.api.discover = function () {
      vm.validated.provider = '';
      vm.api._discoverCount += 1;
      var latest = vm.api._discoverCount;
      var provider = vm.form.provider; // shouldn't be mutable during this time but...

      vm.fn.lock();

      vm.discoveryObj = OAUTH3.urls.discover(provider, vm.conf);
      vm.directivesUrl = OAUTH3.url.normalize(provider) + '/' + vm.discoveryObj.query._pathname;
      vm.discoveryUrl = vm.discoveryObj.method + ' ' + vm.discoveryObj.url;

      console.log('about to discover');

      return OAUTH3.discover(provider, vm.conf).then(function (dir) {
        if (latest !== vm.api._discoverCount) {
          console.log('[DEBUG] ignoring stale discover response for', provider);
          return;
        }
        console.log('[DEBUG] directives:');
        console.log(dir);
        vm.validated.provider = provider;
        vm.directives = dir;
        //JSON.stringify(dir, null, 2);
      }, function (err) {
        if (latest !== vm.api._discoverCount) {
          console.warn('[DEBUG] ignoring stale discover error for', provider);
          console.warn(err);
          return;
        }
        console.log('error on discover');
        vm.error = err;
      }).then(function () {
        vm.fn.unlock();
      });
    };
    vm.api.implicitGrant = function () {
      var provider = vm.validated.provider;
      var opts = {
        client_uri: vm.conf.client_uri
      , subject: vm.form.user
      };

      vm.implicitGrantObj = OAUTH3.urls.implicitGrant(vm.directives, opts);
      console.log('[DEBUG] vm.implicitGrantObj');
      console.log(vm.implicitGrantObj);
      vm.implicitGrantUrl = OAUTH3.url.normalize(provider) + '/' + vm.implicitGrantObj.query._pathname;
      return OAUTH3.implicitGrant(provider, opts);
    };

	} ] );
}());
