/* 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.
 */

/*
 * Controller for morph shape selection
 */

angular.module('flowableModeler').controller('FlowableBpmShapeSelectionCtrl',
    [ '$rootScope', '$scope', '$timeout', '$translate', 'editorManager', function($rootScope, $scope, $timeout, $translate, editorManager) {

    $scope.currentSelectedMorph = undefined;
    
    $scope.availableMorphShapes = [];

    for (var i = 0; i < $scope.morphShapes.length; i++) {
    	if ($scope.morphShapes[i].id != $scope.currentSelectedShape.getStencil().idWithoutNs()) {
    		$scope.availableMorphShapes.push($scope.morphShapes[i]);
    	}
    }
    	
    // Config for grid
    $scope.gridOptions = {
        data: $scope.availableMorphShapes,
        headerRowHeight: 28,
        enableRowSelection: true,
        enableRowHeaderSelection: false,
        multiSelect: false,
        modifierKeysToMultiSelect: false,
        enableHorizontalScrollbar: 0,
		enableColumnMenus: false,
		enableSorting: false,
        columnDefs: [{ field: 'objectId', displayName: 'Icon', width: 50, cellTemplate: 'editor-app/popups/icon-template.html?version=' + Date.now() },
            { field: 'name', displayName: 'Name', cellTemplate: '<div class="ui-grid-cell-contents">{{"" + row.entity[col.field] | translate}}</div>'}]
    };
    
    $scope.gridOptions.onRegisterApi = function(gridApi) {
        //set gridApi on scope
        $scope.gridApi = gridApi;
        gridApi.selection.on.rowSelectionChanged($scope, function(row) {
            if (row.isSelected) {
                $scope.currentSelectedMorph = row.entity;
            } else {
                $scope.currentSelectedMorph = undefined;
            }
        });
    };

    // Click handler for save button
    $scope.select = function() {

        if ($scope.currentSelectedMorph) {
        	var MorphTo = ORYX.Core.Command.extend({
    			construct: function(shape, stencil, facade){
    				this.shape = shape;
    				this.stencil = stencil;
    				this.facade = facade;
    			},
    			execute: function(){
    				
    				var shape = this.shape;
    				var stencil = this.stencil;
    				var resourceId = shape.resourceId;
    				
    				// Serialize all attributes
    				var serialized = shape.serialize();
    				stencil.properties().each((function(prop) {
    					if(prop.readonly()) {
    						serialized = serialized.reject(function(serProp) {
    							return serProp.name==prop.id();
    						});
    					}
    				}).bind(this));
    		
    				// Get shape if already created, otherwise create a new shape
    				if (this.newShape){
    					newShape = this.newShape;
    					this.facade.getCanvas().add(newShape);
    				} else {
    					newShape = this.facade.createShape({
    									type: stencil.id(),
    									namespace: stencil.namespace(),
    									resourceId: resourceId
    								});
    				}
    				
    				// calculate new bounds using old shape's upperLeft and new shape's width/height
    				var boundsObj = serialized.find(function(serProp){
    					return (serProp.prefix === "oryx" && serProp.name === "bounds");
    				});
    				
    				var changedBounds = null;
    				
    				if (!this.facade.getRules().preserveBounds(shape.getStencil())) {
    					
    					var bounds = boundsObj.value.split(",");
    					if (parseInt(bounds[0], 10) > parseInt(bounds[2], 10)) { // if lowerRight comes first, swap array items
    						var tmp = bounds[0];
    						bounds[0] = bounds[2];
    						bounds[2] = tmp;
    						tmp = bounds[1];
    						bounds[1] = bounds[3];
    						bounds[3] = tmp;
    					}
    					bounds[2] = parseInt(bounds[0], 10) + newShape.bounds.width();
    					bounds[3] = parseInt(bounds[1], 10) + newShape.bounds.height();
    					boundsObj.value = bounds.join(",");
    					
    				}  else {
    					
    					var height = shape.bounds.height();
    					var width  = shape.bounds.width();
    					
    					// consider the minimum and maximum size of
    					// the new shape
    					
    					if (newShape.minimumSize) {
    						if (shape.bounds.height() < newShape.minimumSize.height) {
    							height = newShape.minimumSize.height;
    						}
    						
    						
    						if (shape.bounds.width() < newShape.minimumSize.width) {
    							width = newShape.minimumSize.width;
    						}
    					}
    					
    					if (newShape.maximumSize) {
    						if (shape.bounds.height() > newShape.maximumSize.height) {
    							height = newShape.maximumSize.height;
    						}	
    						
    						if (shape.bounds.width() > newShape.maximumSize.width) {
    							width = newShape.maximumSize.width;
    						}
    					}
    					
    					changedBounds = {
    						a : {
    							x: shape.bounds.a.x,
    							y: shape.bounds.a.y
    						},
    						b : {
    							x: shape.bounds.a.x + width,
    							y: shape.bounds.a.y + height
    						}						
    					};
    					
    				}
    				
    				var oPos = shape.bounds.center();
    				if (changedBounds !== null) {
    					newShape.bounds.set(changedBounds);
    				}
    				
    				// Set all related dockers
    				this.setRelatedDockers(shape, newShape);
    				
    				// store DOM position of old shape
    				var parentNode = shape.node.parentNode;
    				var nextSibling = shape.node.nextSibling;
    				
    				// Delete the old shape
    				this.facade.deleteShape(shape);
    				
    				// Deserialize the new shape - Set all attributes
    				newShape.deserialize(serialized);
    				/*
    				 * Change color to default if unchanged
    				 * 23.04.2010
    				 */
    				if (shape.getStencil().property("oryx-bgcolor") 
    						&& shape.properties["oryx-bgcolor"]
    						&& shape.getStencil().property("oryx-bgcolor").value().toUpperCase()== shape.properties["oryx-bgcolor"].toUpperCase()){
    						if (newShape.getStencil().property("oryx-bgcolor")){
    							newShape.setProperty("oryx-bgcolor", newShape.getStencil().property("oryx-bgcolor").value());
    						}
    				}
    				
    				if (changedBounds !== null) {
    					newShape.bounds.set(changedBounds);
    				}
    				
    				if (newShape.getStencil().type()==="edge" || (newShape.dockers.length==0 || !newShape.dockers[0].getDockedShape())) {
    					newShape.bounds.centerMoveTo(oPos);
    				} 
    				
    				if (newShape.getStencil().type()==="node" && (newShape.dockers.length==0 || !newShape.dockers[0].getDockedShape())) {
    					this.setRelatedDockers(newShape, newShape);
    					
    				}
    				
    				// place at the DOM position of the old shape
    				if(nextSibling) parentNode.insertBefore(newShape.node, nextSibling);
    				else parentNode.appendChild(newShape.node);
    				
    				// Set selection
    				this.facade.setSelection([newShape]);
    				this.facade.getCanvas().update();
    				this.facade.updateSelection();
    				this.newShape = newShape;
    				
    			},
    			rollback: function(){
    				
    				if (!this.shape || !this.newShape || !this.newShape.parent) {return;}
    				
    				// Append shape to the parent
    				this.newShape.parent.add(this.shape);
    				// Set dockers
    				this.setRelatedDockers(this.newShape, this.shape);
    				// Delete new shape
    				this.facade.deleteShape(this.newShape);
    				// Set selection
    				this.facade.setSelection([this.shape]);
    				// Update
    				this.facade.getCanvas().update();
    				this.facade.updateSelection();
    			},
    			
    			/**
    			 * Set all incoming and outgoing edges from the shape to the new shape
    			 * @param {Shape} shape
    			 * @param {Shape} newShape
    			 */
    			setRelatedDockers: function(shape, newShape){
    				
    				if(shape.getStencil().type()==="node") {
    					
    					(shape.incoming||[]).concat(shape.outgoing||[])
    						.each(function(i) { 
    							i.dockers.each(function(docker) {
    								if (docker.getDockedShape() == shape) {
    									var rPoint = Object.clone(docker.referencePoint);
    									// Move reference point per percent

    									var rPointNew = {
    										x: rPoint.x*newShape.bounds.width()/shape.bounds.width(),
    										y: rPoint.y*newShape.bounds.height()/shape.bounds.height()
    									};

    									docker.setDockedShape(newShape);
    									// Set reference point and center to new position
    									docker.setReferencePoint(rPointNew);
    									if(i instanceof ORYX.Core.Edge) {
    										docker.bounds.centerMoveTo(rPointNew);
    									} else {
    										var absXY = shape.absoluteXY();
    										docker.bounds.centerMoveTo({x:rPointNew.x+absXY.x, y:rPointNew.y+absXY.y});
    										//docker.bounds.moveBy({x:rPointNew.x-rPoint.x, y:rPointNew.y-rPoint.y});
    									}
    								}
    							});	
    						});
    					
    					// for attached events
    					if(shape.dockers.length>0&&shape.dockers.first().getDockedShape()) {
    						newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
    						newShape.dockers.first().setReferencePoint(Object.clone(shape.dockers.first().referencePoint));
    					}
    				
    				} else { // is edge
    					newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
    					newShape.dockers.first().setReferencePoint(shape.dockers.first().referencePoint);
    					newShape.dockers.last().setDockedShape(shape.dockers.last().getDockedShape());
    					newShape.dockers.last().setReferencePoint(shape.dockers.last().referencePoint);
    				}
    			}
    		});
    		
        	var stencil = undefined;
        	var stencilSets = editorManager.getStencilSets().values();
        	
        	var stencilId = $scope.currentSelectedMorph.id;
        	if ($scope.currentSelectedMorph.genericTaskId) {
        		stencilId = $scope.currentSelectedMorph.genericTaskId;
        	}
        	
        	for (var i = 0; i < stencilSets.length; i++) {
        		var stencilSet = stencilSets[i];
    			var nodes = stencilSet.nodes();
    			for (var j = 0; j < nodes.length; j++) {
    				if (nodes[j].idWithoutNs() === stencilId) {
    					stencil = nodes[j];
    					break;
    				}
            	}
        	}
        	
        	if (!stencil) return;
        	
    		// Create and execute command (for undo/redo)			
    		var command = new MorphTo($scope.currentSelectedShape, stencil, editorManager.getEditor());
    		editorManager.executeCommands([command]);
        }

        $scope.close();
    };

    $scope.cancel = function() {
    	$scope.$hide();
    };

    // Close button handler
    $scope.close = function() {
    	$scope.$hide();
    };

}]);