/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
'use strict';

var activitiModeler = angular.module('activitiModeler', [
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'ngRoute',
  'ngDragDrop',
  'mgcrea.ngStrap', 
  'ngGrid',
  'ngAnimate',
  'pascalprecht.translate',
  'duScroll'
]);

var activitiModule = activitiModeler;

activitiModeler
  // Initialize routes
  .config(['$selectProvider', '$translateProvider', function ($selectProvider, $translateProvider) {

      // Override caret for bs-select directive
      angular.extend($selectProvider.defaults, {
          caretHtml: '&nbsp;<i class="icon icon-caret-down"></i>'
      });
        
        // Initialize angular-translate
        $translateProvider.useStaticFilesLoader({
            prefix: './editor-app/i18n/',
            suffix: '.json'
        });

        $translateProvider.preferredLanguage('en');

        // remember language
        $translateProvider.useCookieStorage();
        
  }])
  .run(['$rootScope', '$timeout', '$modal', '$translate', '$location', '$window', '$http', '$q',
        function($rootScope, $timeout, $modal, $translate, $location, $window, $http, $q) {
	  
			  $rootScope.config = ACTIVITI.CONFIG;
			  
			  $rootScope.editorInitialized = false;
		      
		      $rootScope.editorFactory = $q.defer();
		
		      $rootScope.forceSelectionRefresh = false;
		
		      $rootScope.ignoreChanges = false; // by default never ignore changes
		      
		      $rootScope.validationErrors = [];
		      
		      $rootScope.staticIncludeVersion = Date.now();

			  /**
		       * A 'safer' apply that avoids concurrent updates (which $apply allows).
		       */
		      $rootScope.safeApply = function(fn) {
		          var phase = this.$root.$$phase;
		          if(phase == '$apply' || phase == '$digest') {
		              if(fn && (typeof(fn) === 'function')) {
		                  fn();
		              }
		          } else {
		              this.$apply(fn);
		          }
		      };
	  
	  
            /**
             * Initialize the event bus: couple all Oryx events with a dispatch of the
             * event of the event bus. This way, it gets much easier to attach custom logic
             * to any event.
             */

            /* Helper method to fetch model from server (always needed) */
            function fetchModel(modelId) {

                var modelUrl = KISBPM.URL.getModel(modelId);
                var data = JSON.parse('{"name":"test","revision":1,"description":"u123456","modelId":"1","model":{"resourceId":"1", "stencilset":{"url":"stencilsets/bpmn2.0/bpmn2.0.json","namespace":"http://b3mn.org/stencilset/bpmn2.0#"}}}');
                $rootScope.editor = new ORYX.Editor(data);
                $rootScope.modelData = angular.fromJson(data);
                $rootScope.editorFactory.resolve();
                
                window.$scope = $rootScope;
                window.$editor = $rootScope.editor;
                // $http({method: 'GET', url: modelUrl}).
                //     success(function (data, status, headers, config) {
                //         $rootScope.editor = new ORYX.Editor(data);
                //         $rootScope.modelData = angular.fromJson(data);
                //         $rootScope.editorFactory.resolve();
                //     }).
                //     error(function (data, status, headers, config) {
                //       console.log('Error loading model with id ' + modelId + ' ' + data);
                //     });
            }
            
            window.ibizSetModel = function (data) {
            	if (!$rootScope.editor) {
            		$rootScope.editor = new ORYX.Editor(data);
            		window.$scope = $rootScope;
                    window.$editor = $rootScope.editor;
            	} else {
            		if ($rootScope.editor.getCanvas() && $rootScope.editor.getCanvas().nodes) {
            			$rootScope.editor.getCanvas().nodes.each(function (item) {
            				$rootScope.editor.getCanvas().remove(item);
            			});
            		}
            		if ($rootScope.editor.getCanvas() && $rootScope.editor.getCanvas().edges) {
            			$rootScope.editor.getCanvas().edges.each(function (item) {
            				$rootScope.editor.getCanvas().remove(item);
            			});
            		}
            		$rootScope.editor.setSelection([]);
            		$rootScope.editor.modelMetaData = data;
            		$rootScope.editor.getCanvas().addShapeObjects(data.model.childShapes, $rootScope.editor.handleEvents.bind($rootScope.editor));
            		$rootScope.editor.getCanvas().update();
            	}
                $rootScope.modelData = angular.fromJson(data);
                $rootScope.editorFactory.resolve();
            }


            function initScrollHandling() {
                var canvasSection = jQuery('#canvasSection');
                canvasSection.scroll(function() {

                    // Hides the resizer and quick menu items during scrolling

                    var selectedElements = $rootScope.editor.selection;
                    var subSelectionElements = $rootScope.editor._subSelection;

                    $rootScope.selectedElements = selectedElements;
                    $rootScope.subSelectionElements = subSelectionElements;
                    if (selectedElements && selectedElements.length > 0) {
                    	$rootScope.selectedElementBeforeScrolling = selectedElements[0];
                    }

                    jQuery('.Oryx_button').each(function(i, obj) {
                    	$rootScope.orginalOryxButtonStyle = obj.style.display;
                    	obj.style.display = 'none';
                    });
                    
                    jQuery('.resizer_southeast').each(function(i, obj) {
                    	$rootScope.orginalResizerSEStyle = obj.style.display;
                        obj.style.display = 'none';
                    });
                    jQuery('.resizer_northwest').each(function(i, obj) {
                    	$rootScope.orginalResizerNWStyle = obj.style.display;
                        obj.style.display = 'none';
                    });
                    $rootScope.editor.handleEvents({type:ORYX.CONFIG.EVENT_CANVAS_SCROLL});
                });

                canvasSection.scrollStopped(function(){

                    // Puts the quick menu items and resizer back when scroll is stopped.

                    $rootScope.editor.setSelection([]); // needed cause it checks for element changes and does nothing if the elements are the same
                    $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements);
                    $rootScope.selectedElements = undefined;
                    $rootScope.subSelectionElements = undefined;

                    function handleDisplayProperty(obj) {
                        if (jQuery(obj).position().top > 0) {
                            obj.style.display = 'block';
                        } else {
                            obj.style.display = 'none';
                        }
                    }

                    jQuery('.Oryx_button').each(function(i, obj) {
                        handleDisplayProperty(obj);
                    });
                    
                    jQuery('.resizer_southeast').each(function(i, obj) {
                        handleDisplayProperty(obj);
                    });
                    jQuery('.resizer_northwest').each(function(i, obj) {
                        handleDisplayProperty(obj);
                    });

                });
            }

            /**
             * Initialize the Oryx Editor when the content has been loaded
             */
            $rootScope.$on('$includeContentLoaded', function (event) {
	            if (!$rootScope.editorInitialized) {

	            	ORYX._loadPlugins();
	
	                var modelId = EDITOR.UTIL.getParameterByName('modelId');
	                fetchModel(modelId);
	
	                $rootScope.window = {};
	                var updateWindowSize = function() {
	                    $rootScope.window.width = $window.innerWidth;
	                    $rootScope.window.height  = $window.innerHeight;
	                };
	
	                // Window resize hook
	                angular.element($window).bind('resize', function() {
	                    $rootScope.safeApply(updateWindowSize());
	                });
	
	                $rootScope.$watch('window.forceRefresh', function(newValue) {
	                    if(newValue) {
	                        $timeout(function() {
	                            updateWindowSize();
	                            $rootScope.window.forceRefresh = false;
	                        });
	                    }
	                });
	
	                updateWindowSize();

	                // Hook in resizing of main panels when window resizes
	                // TODO: perhaps move to a separate JS-file?
	                jQuery(window).resize(function () {

	                    // Calculate the offset based on the bottom of the module header
	                    var offset = jQuery("#editor-header").offset();
	                    var propSectionHeight = jQuery('#propertySection').height();
	                    var canvas = jQuery('#canvasSection');
	                    var mainHeader = jQuery('#main-header');

	                    if (offset == undefined || offset === null
	                        || propSectionHeight === undefined || propSectionHeight === null
	                        || canvas === undefined || canvas === null || mainHeader === null) {
	                        return;
	                    }
	                    
	                    if ($rootScope.editor)
	                	{
	        	        	var selectedElements = $rootScope.editor.selection;
	        	            var subSelectionElements = $rootScope.editor._subSelection;
	        	
	        	            $rootScope.selectedElements = selectedElements;
	        	            $rootScope.subSelectionElements = subSelectionElements;
	        	            if (selectedElements && selectedElements.length > 0)
	        	            {
	        	            	$rootScope.selectedElementBeforeScrolling = selectedElements[0];
	        	            	
	        	            	$rootScope.editor.setSelection([]); // needed cause it checks for element changes and does nothing if the elements are the same
	        	                $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements);
	        	                $rootScope.selectedElements = undefined;
	        	                $rootScope.subSelectionElements = undefined;
	        	            }
	                	}

	                    var totalAvailable = jQuery(window).height() - offset.top - mainHeader.height() - 21;
	                    canvas.height(totalAvailable - propSectionHeight);
	                    jQuery('#paletteSection').height(totalAvailable);

	                    // Update positions of the resize-markers, according to the canvas

	                    var actualCanvas = null;
	                    if (canvas && canvas[0].children[1]) {
	                        actualCanvas = canvas[0].children[1];
	                    }

	                    var canvasTop = canvas.position().top;
	                    var canvasLeft = canvas.position().left;
	                    var canvasHeight = canvas[0].clientHeight;
	                    var canvasWidth = canvas[0].clientWidth;
	                    var iconCenterOffset = 8;
	                    var widthDiff = 0;

	                    var actualWidth = 0;
	                    if(actualCanvas) {
	                        // In some browsers, the SVG-element clientwidth isn't available, so we revert to the parent
	                        actualWidth = actualCanvas.clientWidth || actualCanvas.parentNode.clientWidth;
	                    }


	                    if(actualWidth < canvas[0].clientWidth) {
	                        widthDiff = actualWidth - canvas[0].clientWidth;
	                        // In case the canvas is smaller than the actual viewport, the resizers should be moved
	                        canvasLeft -= widthDiff / 2;
	                        canvasWidth += widthDiff;
	                    }

	                    var iconWidth = 17;
	                    var iconOffset = 20;

	                    var north = jQuery('#canvas-grow-N');
	                    north.css('top', canvasTop + iconOffset + 'px');
	                    north.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px');

	                    var south = jQuery('#canvas-grow-S');
	                    south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) +  'px');
	                    south.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px');

	                    var east = jQuery('#canvas-grow-E');
	                    east.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px');
	                    east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px');

	                    var west = jQuery('#canvas-grow-W');
	                    west.css('top', canvasTop -10 + (canvasHeight - iconWidth) / 2 + 'px');
	                    west.css('left', canvasLeft + iconOffset + 'px');

	                    north = jQuery('#canvas-shrink-N');
	                    north.css('top', canvasTop + iconOffset + 'px');
	                    north.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px');

	                    south = jQuery('#canvas-shrink-S');
	                    south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) +  'px');
	                    south.css('left', canvasLeft +10 + (canvasWidth - iconWidth) / 2 + 'px');

	                    east = jQuery('#canvas-shrink-E');
	                    east.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 +  'px');
	                    east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px');

	                    west = jQuery('#canvas-shrink-W');
	                    west.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px');
	                    west.css('left', canvasLeft + iconOffset + 'px');
	                });

	                jQuery(window).trigger('resize');

	                jQuery.fn.scrollStopped = function(callback) {
	                    jQuery(this).scroll(function(){
	                        var self = this, $this = jQuery(self);
	                        if ($this.data('scrollTimeout')) {
	                            clearTimeout($this.data('scrollTimeout'));
	                        }
	                        $this.data('scrollTimeout', setTimeout(callback,50,self));
	                    });
	                };
	                
	                // Always needed, cause the DOM element on which the scroll event listeners are attached are changed for every new model
	                initScrollHandling();
	                
	                $rootScope.editorInitialized = true;
	            }
            });

            /**
             * Initialize the event bus: couple all Oryx events with a dispatch of the
             * event of the event bus. This way, it gets much easier to attach custom logic
             * to any event.
             */

            $rootScope.editorFactory.promise.then(function() {

                KISBPM.eventBus.editor = $rootScope.editor;

                var eventMappings = [
                    { oryxType : ORYX.CONFIG.EVENT_SELECTION_CHANGED, kisBpmType : KISBPM.eventBus.EVENT_TYPE_SELECTION_CHANGE },
                    { oryxType : ORYX.CONFIG.EVENT_DBLCLICK, kisBpmType : KISBPM.eventBus.EVENT_TYPE_DOUBLE_CLICK },
                    { oryxType : ORYX.CONFIG.EVENT_MOUSEOUT, kisBpmType : KISBPM.eventBus.EVENT_TYPE_MOUSE_OUT },
                    { oryxType : ORYX.CONFIG.EVENT_MOUSEOVER, kisBpmType : KISBPM.eventBus.EVENT_TYPE_MOUSE_OVER }

                ];

                eventMappings.forEach(function(eventMapping) {
                    $rootScope.editor.registerOnEvent(eventMapping.oryxType, function(event) {
                        KISBPM.eventBus.dispatch(eventMapping.kisBpmType, event);
                    });
                });
                
                $rootScope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, function (event) {
    	    		var validateButton = document.getElementById(event.shape.resourceId + "-validate-button");
    	    		if (validateButton)
    	    		{
    	    			validateButton.style.display = 'none';
    	    		}
                });

                // The Oryx canvas is ready (we know since we're in this promise callback) and the
                // event bus is ready. The editor is now ready for use
                KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_EDITOR_READY, {type : KISBPM.eventBus.EVENT_TYPE_EDITOR_READY});

            });
            
            // Alerts
            $rootScope.alerts = {
                queue: []
            };
          
            $rootScope.showAlert = function(alert) {
                if(alert.queue.length > 0) {
                    alert.current = alert.queue.shift();
                    // Start timout for message-pruning
                    alert.timeout = $timeout(function() {
                        if (alert.queue.length == 0) {
                            alert.current = undefined;
                            alert.timeout = undefined;
                        } else {
                            $rootScope.showAlert(alert);
                        }
                    }, (alert.current.type == 'error' ? 5000 : 1000));
                } else {
                    $rootScope.alerts.current = undefined;
                }
            };
          
            $rootScope.addAlert = function(message, type) {
                var newAlert = {message: message, type: type};
                if (!$rootScope.alerts.timeout) {
                    // Timeout for message queue is not running, start one
                    $rootScope.alerts.queue.push(newAlert);
                    $rootScope.showAlert($rootScope.alerts);
                } else {
                    $rootScope.alerts.queue.push(newAlert);
                }
            };
          
            $rootScope.dismissAlert = function() {
                if (!$rootScope.alerts.timeout) {
                    $rootScope.alerts.current = undefined;
                } else {
                    $timeout.cancel($rootScope.alerts.timeout);
                    $rootScope.alerts.timeout = undefined;
                    $rootScope.showAlert($rootScope.alerts);
                }
            };
          
            $rootScope.addAlertPromise = function(promise, type) {
                if (promise) {
                    promise.then(function(data) {
                        $rootScope.addAlert(data, type);
                    });
                }
            };
          
        }
  ])

    // Moment-JS date-formatting filter
    .filter('dateformat', function() {
        return function(date, format) {
            if (date) {
                if (format) {
                    return moment(date).format(format);
                } else {
                    return moment(date).calendar();
                }
            }
            return '';
        };
    });
