/*
 * Copyright (c) 2012-2015 Kaarle Ritvanen
 * See LICENSE file for license details
 */

angular.module(
    "aconf", ["ngRoute", "ui.bootstrap", "ui.sortable"]

).config(function($routeProvider) {
    $routeProvider.when("/login", {
	templateUrl: "login.html", controller: "aconfLoginCtrl"
    }).otherwise({templateUrl: "view.html", controller: "aconfObjCtrl"});

}).value("aconfErrorFormat", function(resp) {
    if (_.isString(resp)) return resp;
    if (_.isString(resp.data)) return resp.data;
    if (resp.status == 422) return _.values(resp.data).join("\n");
    return JSON.stringify(resp);

}).factory("aconfExclusive", function($modal, $q, $rootScope) {
    var blocked = false;
    $rootScope.$on("$locationChangeStart", function(event) {
	if (blocked) event.preventDefault();
    });
    function unblock() { blocked = false; }

    function res(task) {
	return $q(function(resolve, reject) {
	    blocked = true;

	    $modal.open({
		backdrop: "static",
		templateUrl: "wait.html",
		controller: function($modalInstance) {
		    $modalInstance.rendered.then(function() {
			task().then(
			    $modalInstance.close, $modalInstance.dismiss
			);
		    });
		    $modalInstance.result.then(resolve, reject);
		    $modalInstance.result.then(unblock, unblock);
		}
	    });
	});
    }
    Object.defineProperty(res, "isRunning", {get: function() {
	return blocked;
    }});
    return res;

}).controller(
    "aconfCtrl",
    function(
	$location, $route, $scope, aconfErrorFormat, aconfExclusive, aconfTxn
    ) {

	$scope.loggedIn = function(token, saveRequired) {
	    $scope.txnMgr = aconfTxn(token, saveRequired);
	    $location.path("/config");
	};

	$scope.validationReady = function(txnValid) {
	    if (txnValid != true) {
		$scope.txnStatus = "invalid";
		$scope.status = aconfErrorFormat(
		    txnValid || "Some values need checking"
		);
	    }
	    else if ($scope.txnMgr.isPristine()) {
		$scope.txnStatus = null;
		$scope.status = "";
	    }
	    else {
		$scope.txnStatus = "changed";
		$scope.status = "There are changes to be committed";
	    }
	};

	function clearState() {
	    $scope.txnStatus = null;
	    $scope.status = "";
	    $route.reload();
	}

	$scope.commit = function() {
	    aconfExclusive(function() {
		return $scope.txnMgr.commit();
	    }).then(clearState, function(resp) {
		$scope.validationReady(
		    "Commit failed: " + aconfErrorFormat(resp)
		);
	    });
	};

	$scope.revert = function() {
	    $scope.txnMgr.abort();
	    clearState();
	};

	$scope.logout = function() {
	    $scope.txnMgr.logout().then(function() {
		$location.path("/login");
	    });
	};
    }
).controller("aconfLoginCtrl", function($http, $scope) {
    $scope.login = function() {
	$http({
	    url: "/login",
	    method: "POST",
	    data: {username: $scope.username, password: $scope.password}
	}).success(function(data, status, headers) {
	    $scope.loggedIn(
		headers("X-AConf-Auth-Token"),
		headers("X-AConf-Save-Required") == "1"
	    );
	}).error(function() {
	    $scope.failed = true;
	    $scope.password = "";
	});
    }
    $("#username").focus();

}).controller(
    "aconfObjCtrl",
    function($location, $q, $scope, aconfPath, aconfType) {
	$scope.txnMgr.start().then(function() {

	    function menuItems(path, current, data) {
		if (data.meta.type != "model") return;

		var items = [];
		var extraFields = false;
		var extraStatus = null;

		function makeItem(path, label, active, klass) {
		    var item = {path: path, label: label};
		    if (active) item.class = "active";
		    else if (klass) item.class = klass;
		    return item;
		}

		_.each(data.meta.fields, function(field) {
		    if (!field.visible) return;
		    var status = data.status(field.name);

		    if (!aconfType.isTreeNode(field)) {
			extraFields = true;
			if (!extraStatus || status == "invalid")
			    extraStatus = status;
		    }
		    else {
			var p = data.get(field.name);
			if (p)
			    items.push(
				makeItem(
				    p,
				    field["ui-name"],
				    aconfPath.isSubordinate(current, p),
				    status
				)
			    );
			field.visible = false;
		    }
		});

		if (!items.length) return;

		if (extraFields)
		    items.unshift(makeItem(
			path, "General", current == path, extraStatus
		    ));
		else if (current == path) {
		    current = items[0].path;
		    items[0].class = "active";
		}

		return {items: items, current: current};
	    };

	    var path = $location.path().substring(7) || "/";
	    $scope.txnMgr.query("/").then(function(data) {
		var modules = menuItems("/", path, data);
		$scope.modules = modules.items;
		if (path == "/") path = modules.current;

		function renderObject(path, data) {
		    (data ? $q.when(data) : $scope.txnMgr.query(path))
			.then(function(data) {
			    $scope.data = data;
			}, function() {
			    var comps = aconfPath.split(path);
			    comps.pop();
			    comps.unshift("/config");
			    $location.path(aconfPath.join.apply(undefined, comps));
			});
		}

		var comps = aconfPath.split(path);

		function renderTabs(p) {
		    p = aconfPath.join(p, comps.shift());
		    $scope.txnMgr.query(p).then(function(data) {
			var menu = menuItems(p, path, data);
			if (menu) {
			    $scope.tabs = menu.items;
			    renderObject(
				menu.current, p == menu.current ? data : null
			    );
			}
			else if (comps.length) renderTabs(p);
			else renderObject(p, data);
		    });
		}

		renderTabs("/");
	    });
	});
    }
).directive("aconfMenu", function() {
    return {
	restrict: "A",
	scope: {aconfMenu: "="},
	templateUrl: "directive/menu.html"
    };
}).directive("aconfLayout", function() {
    return {
	restrict: "E",
	scope: {node: "=", onValidate: "&"},
	template: '<aconf-fields node="node" ng-if="node"></aconf-fields>',
	controller: function($scope) {
	    function validationReady(txnValid) {
		$scope.onValidate({txnValid: txnValid});
	    };

	    this.query = function(path) {
		return $scope.node.txnMgr.query(path);
	    };
	    this.updateRequest = function(task) {
		task.then(validationReady, validationReady);
	    };
	    this.validationReady = validationReady;
	}
    };
}).run(function($location) { $location.path("/login"); });
