var app = angular.module('RestPkiSite', ['ngRoute', 'ngCookies', 'ui.bootstrap', 'blockUI', 'ui.grid', 'pascalprecht.translate', 'ngSanitize', 'ngTable']);

app.config(['$httpProvider', function ($httpProvider) {
	$httpProvider.defaults.headers.common = { 'X-Requested-With': 'XMLHttpRequest' };
}]);

app.config(['$routeProvider', function ($routeProvider) {

	$routeProvider.when('/', {
		templateUrl: views.home,
		controller: 'homeController'
	});

	$routeProvider.when('/signin', {
		templateUrl: views.signIn,
		controller: 'signInController'
	});

	$routeProvider.when('/dashboard', {
		templateUrl: views.dashboard,
		controller: 'dashboardController'
	});

	$routeProvider.when('/system-status', {
		templateUrl: views.systemStatus,
		controller: 'systemStatusController'
	});

	$routeProvider.when('/security-contexts', {
		templateUrl: views.securityContexts,
		controller: 'securityContextsController'
	});

	$routeProvider.when('/security-contexts-admin', {
		templateUrl: views.securityContextsAdmin,
		controller: 'securityContextsAdminController'
	});

	$routeProvider.when('/timestamp-plans', {
		templateUrl: views.timestampPlans,
		controller: 'timestampPlansController'
	});

	$routeProvider.when('/signature-policies', {
		templateUrl: views.signaturePolicies,
		controller: 'signaturePoliciesController'
	});

	$routeProvider.when('/signature-policies-admin', {
		templateUrl: views.signaturePoliciesAdmin,
		controller: 'signaturePoliciesAdminController'
	});

	$routeProvider.when('/transaction-limits', {
		templateUrl: views.transactionLimits,
		controller: 'transactionLimitsController',
	});

	$routeProvider.when('/events', {
		templateUrl: views.events,
		controller: 'EventsController',
		controllerAs: 'vm',
		reloadOnSearch: false
	});

	$routeProvider.when('/users', {
		templateUrl: views.users,
		controller: 'usersController',
		reloadOnSearch: false
	});

	$routeProvider.when('/user-account', {
		templateUrl: views.userAccount,
		controller: 'userAccountController'
	});

	$routeProvider.when('/transactions', {
		templateUrl: views.transactionHistory,
		controller: 'transactionHistoryController',
		reloadOnSearch: false
	});

	$routeProvider.when('/billing', {
		templateUrl: views.billing,
		controller: 'billingController',
		reloadOnSearch: false
	});

	if (!_isOnPremises) {
		$routeProvider.when('/getting-started', {
			templateUrl: views.gettingStarted,
			controller: 'gettingStartedController'
		});
	}

	$routeProvider.otherwise({ redirectTo: "/" });
}]);

app.config(['blockUIConfig', function (blockUIConfig) {
	blockUIConfig.autoBlock = false;
	blockUIConfig.message = 'Please wait ...';
}]);

app.config(['$translateProvider', function ($translateProvider) {
	$translateProvider.registerAvailableLanguageKeys(['en_US', 'pt_BR'], {
		'pt*': 'pt_BR',
		'*': 'en_US'
	});
	$translateProvider.determinePreferredLanguage();
	$translateProvider.useLocalStorage();
}]);

app.factory('lib', ['$http', '$modal', '$rootScope', '$location', '$translate', 'blockUI', 'authService', function ($http, $modal, $rootScope, $location, $translate, blockUI, authService) {

	var tokenKey = 'accessToken';
	var token = null;
	var tokenLoaded = false;

	var showAlert = function (title, message, okCallback) {
		$modal.open({
			templateUrl: views.messageDialog,
			controller: 'messageDialogController',
			resolve: {
				title: function () { return title; },
				message: function () { return message; },
				okCallback: function () { return okCallback; }
			}
		});
	};

	var parseButtons = function (buttons) {
		var buttonObjs = [];
		if (buttons != null) {
			for (var key in buttons) {
				if (buttons.hasOwnProperty(key)) {
					var button = buttons[key];
					buttonObjs.push({
						text: key,
						css: button.css,
						click: button.click
					});
				}
			}
		}
		return buttonObjs;
	};

	var showMessageBox = function (text, caption, buttons) {
		return $modal.open({
			templateUrl: views.messageBox,
			controller: 'messageBoxController',
			resolve: {
				text: function () { return text; },
				caption: function () { return caption; },
				buttons: function () { return parseButtons(buttons); }
			}
		});
	};

	var handleRestError = function (response) {
		blockUI.stop();
		if (response.status === 400) {
			$modal.open({
				templateUrl: views.badRequestDialog,
				controller: 'badRequestDialogController',
				resolve: {
					model: function () { return response.data; }
				}
			});
		} else if (response.status === 401) {
			showAlert('Message', 'Your session has expired', function () {
				authService.logout();
			});
		} else if (response.data.message) {
			showAlert('An error has occurred', response.data.message);
		} else {
			showAlert('An error has occurred', 'HTTP error ' + response.status);
		}
	};

	var handleIdentityError = function (response) {
		blockUI.stop();
		if (response.status === 400 && response.data.message != null) {
			$modal.open({
				templateUrl: views.badRequestDialog,
				controller: 'badRequestDialogController',
				resolve: {
					model: function () { return response.data; }
				}
			});
		} else if (response.data.error_description) {
			showAlert('An error has occurred', response.data.error_description);
		} else if (response.data.error) {
			showAlert('An error has occurred', response.data.error);
		} else if (response.data.message) {
			showAlert('An error has occurred', response.data.message);
		} else {
			showAlert('An error has occurred', 'An error has occurred');
		}
	};

	var showRestError = function (response, callback) {
		blockUI.stop();
		showAlert('An error has occurred', response.data.message, function (modalInstance) {
			modalInstance.close();
			if (callback) {
				callback();
			}
		});
	};

	var showCertificate = function (urlOrModel, downloadUrl, customButtons) {
		$modal.open({
			templateUrl: views.certificateDetails,
			controller: 'certificateDetailsController',
			size: 'lg',
			resolve: {
				urlOrModel: function () { return urlOrModel; },
				downloadUrl: function () { return downloadUrl; },
				customButtons: function () { return parseButtons(customButtons); }
			}
		});
	};

	var checkIsAuthenticated = function () {
		if (authService.isAuthenticated()) {
			return true;
		} else {
			$translate(['message', 'notSignedIn']).then(function (msgs) {
				showAlert(msgs.message, msgs.notSignedIn, function (modalInstance) {
					modalInstance.close();
					$location.path('/signin');
				});
			});
			return false;
		}
	};

	var checkIsAdmin = function () {
		if (!authService.isAuthenticated()) {
			$translate(['message', 'notSignedIn']).then(function (msgs) {
				showAlert(msgs.message, msgs.notSignedIn, function (modalInstance) {
					modalInstance.close();
					$location.path('/signin');
				});
			});
			return false;
		} else if (!authService.isAdmin()) {
			$translate(['accessDenied', 'accessDeniedExtended']).then(function (msgs) {
				showAlert(msgs.accessDenied, msgs.accessDeniedExtended, function (modalInstance) {
					modalInstance.close();
					$location.path('/dashboard');
				});
			});
			return false;
		} else {
			return true;
		}
	};

	var isOnPremises = function () {
		return _isOnPremises; // global variable rendered by Views/App/Index.cshtml
	};

	return {
		showAlert: showAlert,
		showMessageBox: showMessageBox,
		handleRestError: handleRestError,
		handleIdentityError: handleIdentityError,
		showRestError: showRestError,
		showCertificate: showCertificate,
		checkIsAuthenticated: checkIsAuthenticated,
		checkIsAdmin: checkIsAdmin,
		isOnPremises: isOnPremises
	};
}]);

// https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
app.directive('fileModel', ['$parse', function ($parse) {
	return {
		restrict: 'A',
		link: function (scope, element, attrs) {
			var model = $parse(attrs.fileModel);
			var modelSetter = model.assign;

			element.bind('change', function () {
				scope.$apply(function () {
					modelSetter(scope, element[0].files[0]);
				});
			});
		}
	};
}]);
app.service('fileUpload', ['$http', function ($http) {
	this.uploadFileToUrl = function (file, uploadUrl) {
		var fd = new FormData();
		fd.append('file', file);
		return $http.post(uploadUrl, fd, {
			transformRequest: angular.identity,
			headers: { 'Content-Type': undefined }
		});
	};
}]);


// Authentication
//app.factory('authService', [function () {
app.service('authService', ['$location', function ($location) {

	var tokenKey = 'accessToken';
	var usernameKey = 'username';
	var isAdminKey = 'isAdmin';
	var hasPasswordKey = 'hasPassword';
	var userIdKey = 'userId';

	var token = localStorage.getItem(tokenKey);
	var username = localStorage.getItem(usernameKey);
	var isAdmin = (localStorage.getItem(isAdminKey) === 'true');
	var hasPassword = (localStorage.getItem(hasPasswordKey) === 'true');
	var userId = localStorage.getItem(userIdKey);

	this.setAuthenticated = function (tokenArg, userInfo) {
		if (token === tokenArg) {
			return;
		}
		token = tokenArg;
		username = userInfo.username;
		isAdmin = userInfo.isAdmin;
		hasPassword = userInfo.hasPassword;
		localStorage.setItem(tokenKey, tokenArg);
		localStorage.setItem(usernameKey, userInfo.username);
		localStorage.setItem(isAdminKey, userInfo.isAdmin);
		localStorage.setItem(hasPasswordKey, userInfo.hasPassword);
		localStorage.setItem(userIdKey, userInfo.userId);
		$location.path("/dashboard");
	};

	this.getToken = function () {
		return token;
	};

	this.getUsername = function () {
		return username;
	};

	this.isAuthenticated = function () {
		return (token != null);
	};

	this.isAdmin = function () {
		return (token != null) && isAdmin;
	};

	this.hasPassword = function () {
		return (token != null) && hasPassword;
	};

	this.getUserId = function () {
		return userId;
	};

	this.logout = function () {
		localStorage.removeItem(tokenKey);
		localStorage.removeItem(usernameKey);
		localStorage.removeItem(isAdminKey);
		localStorage.removeItem(hasPasswordKey);
		localStorage.removeItem(userIdKey);
		token = null;
		username = null;
		isAdmin = false;
		hasPassword = false;
		location.href = '/';
		userId = null;
	};

	//return {
	//	setAuthenticated: setAuthenticated,
	//	getToken: getToken,
	//	getUsername: getUsername,
	//	isAuthenticated: isAuthenticated,
	//	isAdmin: isAdmin,
	//	logout: logout
	//};

	//this.setAuthenticated = setAuthenticated;
	//this.getToken = getToken;
	//this.getUsername = getUsername;
	//this.isAuthenticated = isAuthenticated;
	//this.isAdmin = isAdmin;
	//this.logout = logout;
}]);

app.service('oauthService', ['$http', 'lib', 'authService', function ($http, lib, authService) {
	this.onLocationChanged = function (currentUrl) {
		var match = /access_token=([A-Za-z0-9\-_]+)/.exec(currentUrl);
		if (match) {
			var t = match[1];
			$http.get('/Api/Account/UserInfo', {
				headers: { 'Authorization': 'Bearer ' + t }
			}).then(function (response) {
				authService.setAuthenticated(t, response.data);
			}, lib.handleIdentityError);
		}
	};
}]);

app.service('httpInterceptor', ['$rootScope', 'authService', function ($rootScope, authService) {
	this.request = function (config) {
		if (authService.isAuthenticated()) {
			config.headers.authorization = 'Bearer ' + authService.getToken();
		}
		return config;
	};
}]);

app.service('notifyService', ['$rootScope', function ($rootScope) {
	PNotify.prototype.options.styling = "bootstrap3";
	PNotify.prototype.options.delay = 3000;
	PNotify.prototype.options.width = '400px';
	//PNotify.defaults.history = false;

	//STACK OPTIONS
	//var stack_topleft = { "dir1": "down", "dir2": "right", "push": "top" };
	//var stack_bottomright = { "dir1": "up", "dir2": "left", "firstpos1": 25, "firstpos2": 25 };
	// var stack_bar_top = { "dir1": "down", "dir2": "right", "push": "top", "spacing1": 10, "spacing2": 10 };
	var stack_center = { "dir1": "down", "dir2": "right", "firstpos1": 25, "firstpos2": ($(window).width() / 2) - (Number(PNotify.prototype.options.width.replace(/\D/g, '')) / 2) };
	$(window).resize(function () {
		stack_center.firstpos2 = ($(window).width() / 2) - (Number(PNotify.prototype.options.width.replace(/\D/g, '')) / 2);
	});


	var notify = function (title, type, text) {
		var opts = {
			addclass: "stack-bar-top",
			stack: stack_center,
			text: text,
			type: type
		};
		new PNotify(opts);
	};

	this.success = function (msg) {
		notify('', 'success', msg);
	};

	this.error = function (msg) {
		notify('', 'error', msg);
	};

	this.warning = function (msg) {
		notify('', 'warning', msg);
	};

	this.info = function (msg) {
		notify('', 'info', msg);
	};
}]);

app.config(['$httpProvider', function ($httpProvider) {
	$httpProvider.interceptors.push('httpInterceptor');
}]);

app.run(['$rootScope', '$route', '$location', 'oauthService', function ($rootScope, $route, $location, oauthService) {
	$rootScope.$on('$locationChangeStart', function (event, next, current) {
		oauthService.onLocationChanged(current);
	});
	$rootScope.$on("$routeChangeStart", function (event, next, current) {
		if (next.originalPath) {
			if (typeof ga !== 'undefined') {
				ga('send', 'pageview', { page: next.originalPath });
			}
		}
	});
}]);

app.directive('lacunaEditableItem', function ($timeout) {
	return {
		restrict: 'E',
		transclude: {
			'label': 'itemLabel',
			'value': 'itemValue',
			'editor': 'itemEditor'
		},
		scope: {
			'description': '@',
			'editable': '=',
			'showDescription': '=',
			'startEdit': '&?onEdit',
			'startSave': '&?onSave'
		},
		templateUrl: '/Content/app/templates/editableItem.html',
		link: function ($scope, element, attrs) {

			if (!$scope.$parent.$editor) {
				$scope.$parent.$editor = {};
			}

			$editor = $scope.$parent.$editor;

			$scope.edit = function () {
				var start = function (data) {
					$editor.active = $scope;
					$editor.data = data;
				};
				if (typeof $scope.startEdit === 'function') {
					$scope.startEdit(attrs).then(start);
				} else {
					start();
				}
			};

			$scope.save = function () {
				if ($scope.isEditing()) {
					if (typeof $scope.startSave === 'function') {
						$scope.startSave($editor.data).then($scope.cancel);
					} else {
						$scope.cancel();
					}
				}
			};

			$scope.cancel = function () {
				if ($scope.isEditing()) {
					$editor.data = {};
					$editor.active = null;
				}
			};

			$scope.isDescriptionVisible = function () {
				return $scope.description && (!angular.isDefined(attrs.showDescription) || $scope.showDescription);
			};

			$scope.isEditable = function () {
				return (!angular.isDefined(attrs.editable) || $scope.editable) && $editor.active == null;
			};

			$scope.isEditing = function () {
				return $editor.active === $scope;
			};

		}
	};
});

app.directive('bsHasError', [function () {
	return {
		restrict: "A",
		link: function (scope, element, attrs, ctrl) {
			var input = element.find('input[ng-model]');
			if (input.length) {
				scope.$watch(function () {
					return input.hasClass('ng-invalid');
				}, function (isInvalid) {
					element.toggleClass('has-error', isInvalid);
				});
			}
		}
	};
}]);

app.directive('lacunaPaginatedList', function ($timeout) {
	return {
		restrict: 'E',
		transclude: true,
		scope: {
			'pageIndex': '=',
			'pageSize': '=',
			'responseData': '<'
		},
		templateUrl: '/Content/app/templates/paginatedList.html',
		link: function ($scope, element, attrs) {
			// these variables are calculated from response data
			var reset = function () {
				$scope.range = { begin: 0, end: 0, total: 0 };
				$scope.pageCount = 1;
				$scope.currentPage = 1;
			};

			// valid page sizes
			$scope.pageSizes = [10, 20, 50, 100];

			// update pagination info
			var update = function () {
				if ($scope.responseData) {
					// grab entity list
					$scope.entities = $scope.responseData.entities || [];
					// calculate visible range
					$scope.range.begin = $scope.responseData.firstIndex || 0;
					$scope.range.total = $scope.responseData.totalCount || 0;
					$scope.range.end = $scope.range.begin + $scope.entities.length - 1;
					// calculate actual page index and count
					$scope.currentPage = Math.floor($scope.range.begin / $scope.pageSize) + 1;
					$scope.pageCount = Math.ceil($scope.range.total / $scope.pageSize);
					// expose
					$scope.$$childHead.$entities = $scope.entities;
				} else {
					// no response, show empty list
					reset();
				}
			};

			// change the page's size
			$scope.setPageSize = function (size) {
				$scope.pageSize = size;
				update();
				$scope.goTo($scope.pageIndex);
			};

			// change the query parameter for the page index
			$scope.goTo = function (index) {
				$scope.pageIndex = Math.max(1, Math.min($scope.pageCount, index));
			};

			// get the selectable pages
			$scope.getPageRange = function () {
				var first = Math.max(1, $scope.currentPage - 2);
				var count = Math.min(5, $scope.pageCount - first + 1);
				return Array.apply(0, Array(count)).map(function (_, i) { return i + first; });
			};

			// watch values
			$scope.$watch('responseData', update, true);

			// init
			reset();

		}
	};
});

app.directive('ngFileModel', ['$parse', function ($parse) {
	return {
		restrict: 'A',
		link: function (scope, element, attrs) {
			var model = $parse(attrs.ngFileModel);
			var isMultiple = attrs.multiple;
			var modelSetter = model.assign;
			element.bind('change', function () {
				scope.$apply(function () {
					if (isMultiple) {
						modelSetter(scope, element[0].files);
					} else {
						modelSetter(scope, element[0].files[0]);
					}
				});
			});
		}
	};
}]);
