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

angular.module("aconf").directive("aconfFields", function() {
    return {
	restrict: "E",
	scope: {node: "=", value: "=path"},
	templateUrl: "directive/fields.html",
	link: function(scope) {
	    scope.layout = scope.node && scope.node.meta.widget || "default";
	}
    }
}).directive("aconfInline", function() {
    return {
	restrict: "E",
	scope: true,
	transclude: true,
	template: '<div class="aconf-node"></div>',
	link: function(scope, element, attrs, ctrl, transclude) {
	    scope.$watch("node", function(node) {
		if (node)
		    transclude(scope, function(clone) {
			element.children().append(clone);
		    });
	    });
	}
    };
}).directive("aconfNode", function() {
    return {
	restrict: "C",
	require: ["?aconfField", "?^^aconfNode", "^^aconfLayout"],
	link: {
	    pre: function(scope, element, attrs, ctrl) {
		var field = ctrl[0];
		var parent = ctrl[1];
		var layout = ctrl[2];

		var fscope = field ? field.scope : scope;

		if (parent) {
		    scope.level = parent.level + 1;
		    layout.query(fscope.value).then(function(node) {
			scope.node = node;
			fscope.node = node;
		    });
		}
		else scope.level = 1;

		scope.$watch("node", function(node) {
		    if (!node) return;

		    scope.checkValidity = function(initial) {
			var valid = node.validate();
			fscope.invalid = !node.isNodeValid();
			if (!(initial && valid)) layout.validationReady(valid);
		    };

		    scope.checkValidity(true);
		});
	    }
	},
	controller: function($scope) {
	    Object.defineProperty(this, "level", {get: function() {
		return $scope.level;
	    }});
	    Object.defineProperty(this, "data", {get: function() {
		return $scope.node;
	    }});
	    this.updated = function(field) {
		$scope.checkValidity();
		$scope.$broadcast("updated", field);
	    };
	}
    };
}).directive("aconfHeader", function() {
    return {
	restrict: "E",
	link: function(scope, element) {
	    element.html(
		$("<h" + Math.min(6, scope.level) + ">")
		    .text(scope.node.meta["ui-name"])
	    );
	    scope.$watch("invalid", function(invalid) {
		if (invalid) element.addClass("invalid");
		else element.removeClass("invalid");
	    });
	}
    };
}).directive("aconfCheckboxes", function() {
    return {
	restrict: "E",
	scope: true,
	templateUrl: "directive/checkboxes.html",
	link: function(scope) {
	    scope.choices = _.filter(
		scope.node.meta.members.choice,
		function(ch) {
		    ch.checked = _.contains(scope.node.data, ch.value);
		    return ch.enabled || ch.checked;
		}
	    );
	}
    };
}).directive("aconfSetCheckbox", function() {
    return {
	restrict: "E",
	scope: {choice: "="},
	templateUrl: "directive/set-checkbox.html",
	link: {
	    pre: function(scope) {
		scope.name = scope.choice.value;
		scope.value = scope.choice.checked;
		scope.set = true;
	    }
	}
    };
}).directive("aconfModel", function() {
    return {
	restrict: "E",
	scope: true,
	transclude: true,
	template: '<div class="aconf-model-fields"></div>',
	link: {
	    pre: function(scope, element, attrs, ctrl, transclude) {
		scope.isRelevant = function(field) {
		    return scope.node.match(field.condition);
		};

		scope.$on("updated", function(event, updatedField) {
		    _.each(scope.fields, function(field, i, fields) {
			if (field.name == updatedField ||
			    !scope.isRelevant(field))
			    return;

			if (field.dynamic)
			    scope.node.metaRequest(field.name).success(
				function(data) { fields[i] = data; }
			    );

			if (field.dynamic ||
			    (field.condition &&
			     updatedField in field.condition))
			    fields[i] = _.clone(field);
		    });
		});

		transclude(scope, function(clone) {
		    element.children().append(clone);
		});
	    }
	}
    };
}).directive("aconfModelFields", function() {
    return {
	restrict: "C",
	require: "?aconfField",
	link: function(scope, element, attrs, ctrl) {
	    if (ctrl) scope = ctrl.scope;
	    scope.$watch("node", function(node) {
		if (!node) return;

		scope.fields = _.where(node.meta.fields, {visible: true});
		scope.columns = _.filter(scope.fields, function(field) {
		    return !(field.detail || field.condition);
		});
		scope.$emit("columns", _.pluck(scope.columns, "ui-name"));

		scope.invoke = function(action) {
		    node.invoke(action).then(function() {
			alert("Done");
		    }, function() { alert("Fail"); });
		};
	    });
	}
    };
}).directive(
    "aconfCollection", function($modal, $q, aconfErrorFormat, aconfType) {
	return {
	    restrict: "E",
	    scope: true,
	    templateUrl: "directive/collection.html",
	    require: "^^aconfLayout",
	    link: function(scope, element, attrs, ctrl) {
		var node = scope.node;
		var meta = node.meta;

		scope.$watch("node.data", function(data) {
		    if (!aconfType.isCollection(meta)) return;

		    scope.fields = _.map(data, function(value, name) {
			if (meta.type == "set") name = data[name];
			else if (_.isArray(data)) name++;
			var set = meta.type == "set";
			return {
			    name: name,
			    label: set ? null : meta["ui-member"] + " " + name,
			    editable: meta.editable && !set,
			    removable: _.contains(meta.removable, name),
			    meta: meta.members
			};
		    });
		}, true);

		if (!meta.editable) return;

		var list = meta.type == "list";
		var set = meta.type == "set";

		function insert(name) {
		    return $q(function(resolve, reject) {
			var task;
			if (aconfType.isTreeNode(meta.members))
			    task = node.set(name, {});
			else if (set) task = node.add(name);

			if (task)
			    task.then(resolve, function(resp) {
				node.delete(name);
				reject(resp);
			    });

			else node.set(name, null).catch(function(resp) {
			    resolve(false);
			});
		    });
		}

		if (list) {
		    scope.$watch(
			"!node.data.length || node.get(node.data.length, true) != null",
			function(saved) {
			    scope.insert = saved && function() {
				ctrl.updateRequest(insert(node.data.length + 1));
			    };
			}
		    );

		    scope.sortable = {
			start: function(event, ui) {
			    var index = ui.item.index();

			    if (!node.isSubtreeValid()) {
				ui.item.sortable.cancel();
				index = -1;
			    }

			    ui.item.data("index", index);
			},
			stop: function(event, ui) {
			    var oldIndex = ui.item.data("index") + 1;
			    var newIndex = ui.item.index() + 1;
			    if (oldIndex && newIndex != oldIndex)
				ctrl.updateRequest(
				    node.move(oldIndex, newIndex)
				);
			}
		    };

		    return;
		}

		scope.insert = function() {
		    $modal.open({
			backdrop: false,
			templateUrl: "insert.html",
			controller: function($scope, $modalInstance) {
			    $scope.submit = function() {
				var name = $scope.value;

				if (set ? _.contains(node.data, name) :
				    name in node.data) {
				    ctrl.validationReady(
					"Already exists: " + name
				    );
				    return;
				}

				insert(name).then(function(txnValid) {
				    $modalInstance.close();
				    ctrl.validationReady(txnValid);
				}, function(resp) {
				    $scope.error = aconfErrorFormat(resp);
				});
			    };

			    $scope.close = function() {
				$modalInstance.dismiss();
			    };
			}
		    }).rendered.then(function($element) {
			$(".modal-body input[type=text]").focus();
		    });
		};
	    }
	};
    }
).directive("aconfTabularLayout", function() {
    return {
	restrict: "E",
	scope: true,
	templateUrl: "directive/tabular-layout.html",
	link: {
	    pre: function(scope) {
		scope.$on("columns", function(event, columns) {
		    scope.columns = columns;
		});
	    }
	}
    };
});
