/*
 * Copyright 2012 LinkedIn, Inc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

var executeFlowView;
var customSvgGraphView;
var customJobListView;
var cloneModel;

function recurseAllAncestors(nodes, disabledMap, id, disable) {
	var node = nodes[id];
	
	if (node.inNodes) {
		for (var key in node.inNodes) {
			disabledMap[key] = disable;
			recurseAllAncestors(nodes, disabledMap, key, disable);
		}
	}
}

function recurseAllDescendents(nodes, disabledMap, id, disable) {
	var node = nodes[id];
	
	if (node.outNodes) {
		for (var key in node.outNodes) {
			disabledMap[key] = disable;
			recurseAllDescendents(nodes, disabledMap, key, disable);
		}
	}
}

azkaban.ContextMenu = Backbone.View.extend({
	events : {
		"click #disableArrow" : "handleDisabledClick",
		"click #enableArrow" : "handleEnabledClick"
	},
	initialize: function(settings) {
		$('#disableSub').hide();
		$('#enableSub').hide();
	},
	handleEnabledClick: function(evt) {
		if(evt.stopPropagation) {
			evt.stopPropagation();
		}
		evt.cancelBubble=true;
		
		if (evt.currentTarget.expanded) {
			evt.currentTarget.expanded=false;
			$('#enableArrow').removeClass('collapse');
			$('#enableSub').hide();
		}
		else {
			evt.currentTarget.expanded=true;
			$('#enableArrow').addClass('collapse');
			$('#enableSub').show();
		}
	},
	handleDisabledClick: function(evt) {
		if(evt.stopPropagation) {
			evt.stopPropagation();
		}
		evt.cancelBubble=true;
		
		if (evt.currentTarget.expanded) {
			evt.currentTarget.expanded=false;
			$('#disableArrow').removeClass('collapse');
			$('#disableSub').hide();
		}
		else {
			evt.currentTarget.expanded=true;
			$('#disableArrow').addClass('collapse');
			$('#disableSub').show();
		}
	}
});

azkaban.ExecuteFlowView = Backbone.View.extend({
  	  events : {
  	  	"click" : "closeEditingTarget",
	    "click #execute-btn": "handleExecuteFlow",
	    "click #cancel-btn": "handleCancelExecution",
	    "click .modal-close": "handleCancelExecution",
	    "click #generalOptions": "handleGeneralOptionsSelect",
	    "click #flowOptions": "handleFlowOptionsSelect",
	    "click #addRow": "handleAddRow",
	    "click table .editable": "handleEditColumn",
	    "click table .removeIcon": "handleRemoveColumn"
	  },
	  initialize: function(setting) {
	  	 this.contextMenu = new azkaban.ContextMenu({el:$('#disableJobMenu')});
	  	 this.handleGeneralOptionsSelect();
	  },
	  show: function() {
	  	this.handleGeneralOptionsSelect();
	  	$('#modalBackground').show();
	  	$('#executing-options').show();
	  	this.cloneModel = this.model.clone();
	  	cloneModel = this.cloneModel;
	  	
	  	var fetchData = {"project": projectName, "ajax":"flowInfo", "flow":flowName};
	  	if (execId) {
	  		fetchData.execid = execId;
	  	}
	  	var executeURL = contextURL + "/executor";
	  	var handleAddRow = this.handleAddRow;
	  	
	  	var data = this.cloneModel.get("data");
	  	var nodes = {};
	  	for (var i=0; i < data.nodes.length; ++i) {
	      	var node = data.nodes[i];
	      	nodes[node.id] = node;
	    }
      	
      	for (var i=0; i < data.edges.length; ++i) {
      	  	var edge = data.edges[i];
      	  	var fromNode = nodes[edge.from];
      	  	var toNode = nodes[edge.target];
      	  	
      	  	if (!fromNode.outNodes) {
      	  		fromNode.outNodes = {};
      	  	}
      	  	fromNode.outNodes[toNode.id] = toNode;
      	  	
      	  	if (!toNode.inNodes) {
      	  		toNode.inNodes = {};
      	  	}
      	  	toNode.inNodes[fromNode.id] = fromNode;
      	}
	    this.cloneModel.set({nodes: nodes});
	  	
	  	var disabled = {};
		for (var i = 0; i < data.nodes.length; ++i) {
			var updateNode = data.nodes[i];
			if (updateNode.status == "DISABLED" || updateNode.status == "SKIPPED") {
				updateNode.status = "READY";
				disabled[updateNode.id] = true;
			}
			if (updateNode.status == "SUCCEEDED") {
				disabled[updateNode.id] = true;
			}
		}
	  	this.cloneModel.set({disabled: disabled});
	  	
	  	$.get(
			executeURL,
			fetchData,
			function(data) {
				if (data.error) {
					alert(data.error);
				}
				else {
					if (data.successEmails) {
						$('#successEmails').val(data.successEmails.join());
					}
					if (data.failureEmails) {
						$('#failureEmails').val(data.failureEmails.join());
					}
					
					if (data.failureAction) {
						$('#failureAction').val(data.failureAction);
					}
					if (data.notifyFailureFirst) {
						$('#notifyFailureFirst').attr('checked', true);
					}
					if (data.notifyFailureLast) {
						$('#notifyFailureLast').attr('checked', true);	
					}
					if (data.flowParam) {
						var flowParam = data.flowParam;
						for (var key in flowParam) {
							var row = handleAddRow();
							var td = $(row).find('td');
							$(td[0]).text(key);
							$(td[1]).text(flowParam[key]);
						}
					}

					if (!data.running || data.running.length == 0) {
						$(".radio").attr("disabled", "disabled");
						$(".radioLabel").addClass("disabled", "disabled");
					}
				}
			},
			"json"
		);
	  },
	  handleCancelExecution: function(evt) {
	  	var executeURL = contextURL + "/executor";
		$('#modalBackground').hide();
	  	$('#executing-options').hide();
	  },
	  handleGeneralOptionsSelect: function(evt) {
	  	$('#flowOptions').removeClass('selected');
	  	$('#generalOptions').addClass('selected');

	  	$('#generalPanel').show();	  	
	  	$('#graphPanel').hide();
	  },
	  handleFlowOptionsSelect: function(evt) {
	  	$('#generalOptions').removeClass('selected');
	  	$('#flowOptions').addClass('selected');

	  	$('#graphPanel').show();	  	
	  	$('#generalPanel').hide();
	  	
	  	if (this.flowSetup) {
	  		return;
	  	}
	  	
	  	customSvgGraphView = new azkaban.SvgGraphView({el:$('#svgDivCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
		customJobsListView = new azkaban.JobListView({el:$('#jobListCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
		this.cloneModel.trigger("change:graph");
		
		this.flowSetup = true;
	  },
	  handleExecuteFlow: function(evt) {
	  	var executeURL = contextURL + "/executor";
	  	var disabled = this.cloneModel.get("disabled");
	  	var failureAction = $('#failureAction').val();
	  	var failureEmails = $('#failureEmails').val();
	  	var successEmails = $('#successEmails').val();
	  	var notifyFailureFirst = $('#notifyFailureFirst').is(':checked');
	  	var notifyFailureLast = $('#notifyFailureLast').is(':checked');
	  	var executingJobOption = $('input:radio[name=gender]:checked').val();
	  	
	  	var flowOverride = {};
	  	var editRows = $(".editRow");
		for (var i = 0; i < editRows.length; ++i) {
			var row = editRows[i];
			var td = $(row).find('td');
			var key = $(td[0]).text();
			var val = $(td[1]).text();
			
			if (key && key.length > 0) {
				flowOverride[key] = val;
			}
		}
	  	
	  	var executingData = {
	  		project: projectName,
	  		ajax: "executeFlow",
	  		flow: flowName,
	  		disable: this.cloneModel.get('disabled'),
	  		failureAction: failureAction,
	  		failureEmails: failureEmails,
	  		successEmails: successEmails,
	  		notifyFailureFirst: notifyFailureFirst,
	  		notifyFailureLast: notifyFailureLast,
	  		executingJobOption: executingJobOption,
	  		flowOverride: flowOverride
	  	};
	  	
		$.get(
			executeURL,
			executingData,
			function(data) {
				if (data.error) {
					alert(data.error);
				}
				else {
					var redirectURL = contextURL + "/executor?execid=" + data.execid;
					window.location.href = redirectURL;
				}
			},
			"json"
		);
	  },
	  handleAddRow: function(evt) {
	  	var tr = document.createElement("tr");
	  	var tdName = document.createElement("td");
	    var tdValue = document.createElement("td");
	    
	    var icon = document.createElement("span");
	    $(icon).addClass("removeIcon");
	    var nameData = document.createElement("span");
	    $(nameData).addClass("spanValue");
	    var valueData = document.createElement("span");
	    $(valueData).addClass("spanValue");
	    	    
		$(tdName).append(icon);
		$(tdName).append(nameData);
		$(tdName).addClass("name");
		$(tdName).addClass("editable");
		
		$(tdValue).append(valueData);
	    $(tdValue).addClass("editable");
		
		$(tr).addClass("editRow");
	  	$(tr).append(tdName);
	  	$(tr).append(tdValue);
	   
	  	$(tr).insertBefore("#addRow");
	  	return tr;
	  },
	  handleEditColumn : function(evt) {
	  	var curTarget = evt.currentTarget;
	
	  	if (this.editingTarget != curTarget) {
			this.closeEditingTarget();
			
			var text = $(curTarget).children(".spanValue").text();
			$(curTarget).empty();
						
			var input = document.createElement("input");
			$(input).attr("type", "text");
			$(input).css("width", "100%");
			$(input).val(text);
			$(curTarget).addClass("editing");
			$(curTarget).append(input);
			$(input).focus();
			this.editingTarget = curTarget;
	  	}
	  },
	  handleRemoveColumn : function(evt) {
	  	var curTarget = evt.currentTarget;
	  	// Should be the table
	  	var row = curTarget.parentElement.parentElement;
		$(row).remove();
	  },
	  closeEditingTarget: function(evt) {
	  	if (this.editingTarget != null && this.editingTarget != evt.target && this.editingTarget != evt.target.parentElement ) {
	  		var input = $(this.editingTarget).children("input")[0];
	  		var text = $(input).val();
	  		$(input).remove();

		    var valueData = document.createElement("span");
		    $(valueData).addClass("spanValue");
		    $(valueData).text(text);

	  		if ($(this.editingTarget).hasClass("name")) {
		  		var icon = document.createElement("span");
		    	$(icon).addClass("removeIcon");
		    	$(this.editingTarget).append(icon);
		    }
		    
		    $(this.editingTarget).removeClass("editing");
		    $(this.editingTarget).append(valueData);
		    this.editingTarget = null;
	  	}
	  },
	  handleDisableMenuClick : function(action, el, pos) {
			var jobid = el[0].jobid;
			var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowName + "&job=" + jobid;
			if (action == "open") {
				window.location.href = requestURL;
			}
			else if(action == "openwindow") {
				window.open(requestURL);
			}
			else if(action == "disable") {
				var disabled = cloneModel.get("disabled");
		
				disabled[jobid] = true;
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if(action == "disableAll") {
				var disabled = cloneModel.get("disabled");
		
				var nodes = cloneModel.get("nodes");
				for (var key in nodes) {
					disabled[key] = true;
				}

				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "disableParents") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				var inNodes = nodes[jobid].inNodes;
		
				if (inNodes) {
					for (var key in inNodes) {
					  disabled[key] = true;
					}
				}
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "disableChildren") {
				var disabledMap = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				var outNodes = nodes[jobid].outNodes;
		
				if (outNodes) {
					for (var key in outNodes) {
					  disabledMap[key] = true;
					}
				}
				
				cloneModel.set({disabled: disabledMap});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "disableAncestors") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				
				recurseAllAncestors(nodes, disabled, jobid, true);
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "disableDescendents") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				
				recurseAllDescendents(nodes, disabled, jobid, true);
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if(action == "enable") {
				var disabled = cloneModel.get("disabled");
		
				disabled[jobid] = false;
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if(action == "enableAll") {
				disabled = {};
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "enableParents") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				var inNodes = nodes[jobid].inNodes;
		
				if (inNodes) {
					for (var key in inNodes) {
					  disabled[key] = false;
					}
				}
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "enableChildren") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				var outNodes = nodes[jobid].outNodes;
		
				if (outNodes) {
					for (var key in outNodes) {
					  disabled[key] = false;
					}
				}
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "enableAncestors") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				
				recurseAllAncestors(nodes, disabled, jobid, false);
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
			else if (action == "enableDescendents") {
				var disabled = cloneModel.get("disabled");
				var nodes = cloneModel.get("nodes");
				
				recurseAllDescendents(nodes, disabled, jobid, false);
				
				cloneModel.set({disabled: disabled});
				cloneModel.trigger("change:disabled");
			}
		}
});