(function() {
   
   var util = YAHOO.util;
   var Dom = util.Dom, Event = util.Event, CSS_PREFIX = "WireIt-";
   
/**
 * Visual module that contains terminals. The wires are updated when the module is dragged around.
 * @class Container
 * @namespace WireIt
 * @constructor
 * @param {Object}   options      Configuration object (see options property)
 * @param {WireIt.Layer}   layer The WireIt.Layer (or subclass) instance that contains this container
 */
WireIt.Container = function(options, layer) {
   
   // Set the options
   this.setOptions(options);
   
   /**
    * the WireIt.Layer object that schould contain this container
    * @property layer
    * @type {WireIt.Layer}
    */
   this.layer = layer;
   
   /**
    * List of the terminals 
    * @property terminals
    * @type {Array}
    */
   this.terminals = [];
   
   /**
    * List of all the wires connected to this container terminals
    * @property wires
    * @type {Array}
    */
   this.wires = [];
   
   /**
    * Container DOM element
    * @property el
    * @type {HTMLElement}
    */
   this.el = null;
   
   /**
    * Body element
    * @property bodyEl
    * @type {HTMLElement}
    */
   this.bodyEl = null;
   
   /**
    * Event that is fired when a wire is added
    * You can register this event with myTerminal.eventAddWire.subscribe(function(e,params) { var wire=params[0];}, scope);
    * @event eventAddWire
    */
   this.eventAddWire = new util.CustomEvent("eventAddWire");
   
   /**
    * Event that is fired when a wire is removed
    * You can register this event with myTerminal.eventRemoveWire.subscribe(function(e,params) { var wire=params[0];}, scope);
    * @event eventRemoveWire
    */
   this.eventRemoveWire = new util.CustomEvent("eventRemoveWire");
   
   // Render the div object
   this.render();
   
   // Init the terminals
   this.initTerminals( this.options.terminals);
   
	// Make the container draggable
	if(this.options.draggable) {
		   
	   if(this.options.resizable) {
      	// Make resizeable   
      	this.ddResize = new WireIt.util.DDResize(this);
      	this.ddResize.eventResize.subscribe(this.onResize, this, true);
	   }
	   
	   // Use the drag'n drop utility to make the container draggable
	   this.dd = new WireIt.util.DD(this.terminals,this.el);
	   
	   // Sets ddHandle as the drag'n drop handle
	   if(this.options.ddHandle) {
   	   this.dd.setHandleElId(this.ddHandle);
	   }
	   
	   // Mark the resize handle as an invalid drag'n drop handle and vice versa
	   if(this.options.resizable) {
   	   this.dd.addInvalidHandleId(this.ddResizeHandle);
      	this.ddResize.addInvalidHandleId(this.ddHandle);
	   }
   }
   
};

WireIt.Container.prototype = {
   
   /**
    * set the options
    * @method setOptions
    */
   setOptions: function(options) {
      
      /**
       * Main options object
       * <ul>
       *    <li>terminals: list of the terminals configuration</li>
       *    <li>draggable: boolean that enables drag'n drop on this container (default: true)</li>
       *    <li>className: CSS class name for the container element (default 'WireIt-Container')</li>
       *    <li>position: initial position of the container</li>
       *    <li>ddHandle: (only if draggable) boolean indicating we use a handle for drag'n drop (default true)</li>
       *    <li>ddHandleClassName: CSS class name for the drag'n drop handle (default 'WireIt-Container-ddhandle')</li>
       *    <li>resizable: boolean that makes the container resizable (default true)</li>
       *    <li>resizeHandleClassName: CSS class name for the resize handle (default 'WireIt-Container-resizehandle')</li>
       *    <li>width: initial width of the container (no default so it autoadjusts to the content)</li>
       *    <li>height: initial height of the container (default 100)</li>
       *    <li>close: display a button to close the container (default true)</li>
       *    <li>closeButtonClassName: CSS class name for the close button (default "WireIt-Container-closebutton")</li>
       *    <li>title: text that will appear in the module header</li>
       *    <li>icon: image url to be displayed in the module header</li>
       *    <li>preventSelfWiring: option to prevent connections between terminals of this same container (default true)</li>
       * </ul>
       * @property options
       * @type {Object}
       */
      this.options = {};
      this.options.terminals = options.terminals || [];
      this.options.draggable = (typeof options.draggable == "undefined") ? true : options.draggable ;
      this.options.position = options.position || [100,100];
      this.options.className = options.className || CSS_PREFIX+'Container';

      this.options.ddHandle = (typeof options.ddHandle == "undefined") ? true : options.ddHandle;
      this.options.ddHandleClassName = options.ddHandleClassName || CSS_PREFIX+"Container-ddhandle";

      this.options.resizable = (typeof options.resizable == "undefined") ? true : options.resizable;
      this.options.resizeHandleClassName = options.resizeHandleClassName || CSS_PREFIX+"Container-resizehandle";

      this.options.width = options.width; // no default
      this.options.height = options.height;

      this.options.close = (typeof options.close == "undefined") ? true : options.close;
      this.options.closeButtonClassName = options.closeButtonClassName || CSS_PREFIX+"Container-closebutton";

	  // store entity_guid for cvltvre map tool //
      this.options.title = options.title; // no default
      
      this.options.icon = options.icon;

      this.options.preventSelfWiring = (typeof options.preventSelfWiring == "undefined") ? true : options.preventSelfWiring;

	  //*************************************//
	  // added for cvltvre map tool			 //
	  this.options.clickable = (typeof options.clickable == "undefined") ? false : options.clickable;
	  this.options.innerView = (typeof options.innerView == "undefined") ? '' : options.innerView;
	  this.options.maximize = (typeof options.maximize == "undefined") ? false : options.maximize;  
	  this.options.maximize_css = (typeof options.maximize_css == "undefined") ? 'WireIt-Container-maximizebutton-full' : options.maximize_css;  
  	  this.options.associations = (typeof options.associations == "undefined") ? '' : options.associations;  
      this.options.offset = (typeof options.offset == "undefined") ? '' : options.offset;  
	  this.options.entityId = (typeof options.entityId == "undefined") ? '' : options.entityId;
	  this.options.parentId = (typeof options.parentId == "undefined") ? '' : options.parentId;	 
	  this.options.history = (typeof options.history == "undefined") ? false : options.history; 
	  //*************************************//
   },

   /**
    * Function called when the container is being resized.
    * It doesn't do anything, so please override it.
    * @method onResize
    */
   onResize: function(event, args) {
      var size = args[0];
      WireIt.sn(this.bodyEl, null, {width: (size[0]-10)+"px", height: (size[1]-44)+"px"});
   },

   /**
    * Render the dom of the container
    * @method render
    */
   render: function() {
   
      // Create the element
      this.el = WireIt.cn('div', {className: this.options.className});
   
      if(this.options.width) {
         this.el.style.width = this.options.width+"px";
      }
      if(this.options.height) {
         this.el.style.height = this.options.height+"px";
      }

   
      if(this.options.ddHandle) {
         // Create the drag/drop handle
      	this.ddHandle = WireIt.cn('div', {className: this.options.ddHandleClassName});
      	this.el.appendChild(this.ddHandle);
      	
         // Set title
         if(this.options.title) {
            this.ddHandle.appendChild( WireIt.cn('span', null, null, this.options.title) );
         }
         
         // Icon
         if (this.options.icon) {
            var iconCn = WireIt.cn('img', {src: this.options.icon, className: 'WireIt-Container-icon'});
            this.ddHandle.appendChild(iconCn);
         }

      }
	  
      // Add maximize/expand image to container and it's events if expandable
      if (this.options.maximize) {
      
          this.maximizeButton = WireIt.cn('div', {
              className: this.options.maximize_css},'',"<div id='event_listing_item' style='position:absolute'><table><tr><td class='main'><img src='../../vendors/wireit/images/plus-small.jpg' /></td><td style='font-weight: bold; font-size: 15px;padding-left: 5px;'>"+this.options.associations+"</td></tr></table></div>");
          this.el.appendChild(this.maximizeButton);

          Event.addListener(this.maximizeButton, "click", this.onClick, this, true);
          Event.addListener(this.maximizeButton, "mouseover", this.onMouseOver, this, true);
          Event.addListener(this.maximizeButton, "mouseout", this.onMouseOut, this, true);
      }  

	  
// option added for cvltvre specifically
// TODO: overflow = hidden/auto
if (this.options.clickable) {

	if (this.options.innerView == '') {
		this.bodyEl = WireIt.cn('div', {
			className: "body"
		}, {
			width: this.options.width + "px",
			height: this.options.height + "px"
		});
	}else{
		this.bodyEl = WireIt.cn('div', {
			className: "body"
		}, {
			width: this.options.width + "px",
			height: this.options.height + "px",
			overflow: "hidden"
		}, this.options.innerView);
	}
	
    this.el.appendChild(this.bodyEl);
	if ((this.options.title === 'more') || (this.options.title === 'less')) {
    	Event.addListener(this.bodyEl, "click", this.onClick, this, true);
    }  
	Event.addListener(this.bodyEl, "mouseover", this.onMouseOver, this, true);
	Event.addListener(this.bodyEl, "mouseout", this.onMouseOut, this, true);

}
//else keep original container body element
else {
    // Create the body element
    this.bodyEl = WireIt.cn('div', {
        className: "body"
    });
    this.el.appendChild(this.bodyEl);
	   
      // Adds a handler for mousedown so we can notice the layer
      Event.addListener(this.el, "mousedown", this.onMouseDown, this, true);
}
   
      if(this.options.resizable) {
         // Create the resize handle
      	this.ddResizeHandle = WireIt.cn('div', {className: this.options.resizeHandleClassName} );
      	this.el.appendChild(this.ddResizeHandle);
      }
   
      if(this.options.close) {

         this.closeButton = WireIt.cn('div', {className: this.options.closeButtonClassName} );
         this.el.appendChild(this.closeButton);
         Event.addListener(this.closeButton, "click", this.onCloseButton, this, true);
      }
   
      // Append to the layer element
      this.layer.el.appendChild(this.el);
   
   	// Set the position
   	this.el.style.left = this.options.position[0]+"px";
   	this.el.style.top = this.options.position[1]+"px";
   },

   /**
    * Sets the content of the body element
    * @method setBody
    * @param {String or HTMLElement} content
    */
   setBody: function(content) {
      if(typeof content == "string") {
         this.bodyEl.innerHTML = content;
      }
      else {
         this.bodyEl.innerHTML = "";
         this.bodyEl.appendChild(content);
      }
   },

   /**
    * Called when the user made a mouse down on the container and sets the focus to this container (only if within a Layer)
    * @method onMouseDown
    */
	onMouseDown: function() {
      if(this.layer) {
         if(this.layer.focusedContainer && this.layer.focusedContainer != this) {
            this.layer.focusedContainer.removeFocus();
         }
         this.setFocus();
         this.layer.focusedContainer = this;
      }
   },
   
   // added for entity containers for cvltvre   
   onClick: function() {
	  //onMouseDown();
	  
	  if ((this.options.title == "more") || (this.options.title == "less")) {
	  	getConnections(this.options.entityId, this, this.options.offset);
	  }
	  else {
	   var historyIndex = historyItems.indexOf(this.options.title);
	  	if(historyIndex == -1){
			historyItems.push(this.options.parentId);
		}else{
			for(var i=historyItems.length; i>historyIndex; i--){
				historyItems.pop();
			}
		}
	  	getConnections(this.options.title, this, 0);
	  }
   },
   
   // added for entity containers for cvltvre
   onMouseOver: function(){
/*
	if (this.options.history) {
		Dom.removeClass(this.el,"WireIt-History-Container");
	}
	else {
		Dom.removeClass(this.el, CSS_PREFIX + "Container");
	}
	Dom.addClass(this.el, CSS_PREFIX+"Container-Enabled");
	*/
	
	var terminal1 = this.terminals[0];
	
	if (terminal1.options.name !== "terminal0") {
		if (this.wires.length != 0) {

			var newContainers = [];
			var j=0;
			while(this.wires.length != 0)
			{
				var terminal2 = this.wires[0].getOtherTerminal(terminal1);
				var container2 = this.getContainer(terminal2);
				newContainers[j] = {
					"container": container2,
					"terminal": terminal2
				};
				this.wires[0].remove();
				j++;
			}
			
			for(var k=0;k<newContainers.length; k++)
			{
				var newWire = this.layer.addWire({
					"xtype": "WireIt.Wire",
					"src": {
						element: newContainers[k].container ,
						terminal: newContainers[k].terminal.options.name
					},
					"tgt": {
						element: this,
						terminal: terminal1.options.name
					},
					"options": {
						width: 2,
						coeffMulDirection: 20,
						borderwidth: 0,
						color: 'rgba(0, 0, 0, 1)'
					}
				});
			}
		}
	}
	
   },
   
   // added for entity containers for cvltvre
   onMouseOut: function() {

   	Dom.removeClass(this.el, CSS_PREFIX+"Container-Enabled");
	var containerColor;
/*	if (this.options.history) {
		containerColor = 'rgba(255, 192, 100, 0.8)';
		Dom.addClass(this.el,"WireIt-History-Container");
	}
	else {
		containerColor = 'rgba(173, 216, 230, 0.3)';
		Dom.addClass(this.el, CSS_PREFIX + "Container");
	}*/
	var terminal1 = this.terminals[0];
	if (terminal1.options.name !== "terminal0") {
		if (this.wires.length != 0) {
			
			var newContainers =[];
			var j=0;
			while(this.wires.length != 0)
			{
				var terminal2 = this.wires[0].getOtherTerminal(terminal1);
				var container2 = this.getContainer(terminal2);
				newContainers[j] = {
					"container": container2,
					"terminal": terminal2
				};
				this.wires[0].remove();
				j++;
			}
			
			for(var k=0;k< newContainers.length; k++)
			{
				var newWire = this.layer.addWire({
					"xtype": "WireIt.Wire",
					"src": {
						element: newContainers[k].container ,
						terminal: newContainers[k].terminal.options.name
					},
					"tgt": {
						element: this,
						terminal: terminal1.options.name
					},
					"options": {
						width: 2,
						coeffMulDirection: 20,
						borderwidth: 0,
						color: containerColor
					}
				});
			}
		}
	}
   },

   /**
    * Adds the class that shows the container as "focused"
    * @method setFocus
    */
   setFocus: function() {
      Dom.addClass(this.el, CSS_PREFIX+"Container-focused");
   },

   /**
    * Remove the class that shows the container as "focused"
    * @method removeFocus
    */
   removeFocus: function() {
      Dom.removeClass(this.el, CSS_PREFIX+"Container-focused");
   },

   /**
    * Called when the user clicked on the close button
    * @method onCloseButton
    */
   onCloseButton: function(e, args) {
      Event.stopEvent(e);
      this.layer.removeContainer(this);
   },

   /**
    * Remove this container from the dom
    * @method remove
    */
   remove: function() {
   
      // Remove the terminals (and thus remove the wires)
      this.removeAllTerminals();
   
      // Remove from the dom
      this.layer.el.removeChild(this.el);
      
      // Remove all event listeners
      Event.purgeElement(this.el);
   },


   /**
    * Call the addTerminal method for each terminal configuration.
    * @method initTerminals
    */
   initTerminals: function(terminalConfigs) {
      for(var i = 0 ; i < terminalConfigs.length ; i++) {
         this.addTerminal(terminalConfigs[i]);
      }
   },


   /**
    * Instanciate the terminal from the class pointer "xtype" (default WireIt.Terminal)
    * @method addTerminal
    * @return {WireIt.Terminal}  terminal Created terminal
    */
   addTerminal: function(terminalConfig) {
   
      // Terminal type
      var type = eval(terminalConfig.xtype || "WireIt.Terminal");
   
      // Instanciate the terminal
      var term = new type(this.el, terminalConfig, this);
   
      // Add the terminal to the list
      this.terminals.push( term );
   
      // Event listeners
      term.eventAddWire.subscribe(this.onAddWire, this, true);
      term.eventRemoveWire.subscribe(this.onRemoveWire, this, true);
   
      return term;
   },

   /**
    * This method is called when a wire is added to one of the terminals
    * @method onAddWire
    * @param {Event} event The eventAddWire event fired by the terminal
    * @param {Array} args This array contains a single element args[0] which is the added Wire instance
    */
   onAddWire: function(event, args) {
      var wire = args[0];
      // add the wire to the list if it isn't in
      if( WireIt.indexOf(wire, this.wires) == -1 ) {
         this.wires.push(wire);
         this.eventAddWire.fire(wire);
      } 
   },

   /**
    * This method is called when a wire is removed from one of the terminals
    * @method onRemoveWire
    * @param {Event} event The eventRemoveWire event fired by the terminal
    * @param {Array} args This array contains a single element args[0] which is the removed Wire instance
    */
   onRemoveWire: function(event, args) {
      var wire = args[0];
      var index = WireIt.indexOf(wire, this.wires);
      if( index != -1 ) {
         this.eventRemoveWire.fire(wire);
         this.wires[index] = null;
      }
      this.wires = WireIt.compact(this.wires);
   },

   /**
    * Remove all terminals
    * @method removeAllTerminals
    */
   removeAllTerminals: function() {
      for(var i = 0 ; i < this.terminals.length ; i++) {
         this.terminals[i].remove();
      }
      this.terminals = [];
   },

   /**
    * Redraw all the wires connected to the terminals of this container
    * @method redrawAllTerminals
    */
   redrawAllWires: function() {
      for(var i = 0 ; i < this.terminals.length ; i++) {
         this.terminals[i].redrawAllWires();
      }
   },

   /**
    * Return the config of this container.
    * @method getConfig
    */
   getConfig: function() {
      var obj = {};
   
      // Position
      obj.position = Dom.getXY(this.el);
      if(this.layer) {
         // remove the layer position to the container position
         var layerPos = Dom.getXY(this.layer.el);
         obj.position[0] -= layerPos[0];
         obj.position[1] -= layerPos[1];
         // add the scroll position of the layer to the container position
         obj.position[0] += this.layer.el.scrollLeft;
         obj.position[1] += this.layer.el.scrollTop;
      }
   
      // xtype
      if(this.options.xtype) {
         obj.xtype = this.options.xtype;
      }
   
      return obj;
   },
   
   /**
    * Subclasses should override this method.
    * @method getValue
    * @return {Object} value
    */
   getValue: function() {
      return {};
   },

   /**
    * Subclasses should override this method.
    * @method setValue
    * @param {Any} val Value 
    */
   setValue: function(val) {
   },
   
   
   /**
    * @method getTerminal
    */
   getTerminal: function(name) {
      var term;
      for(var i = 0 ; i < this.terminals.length ; i++) {
         term = this.terminals[i];
         if(term.options.name == name) {
            return term;
         }
      }
      return null;
   },
   
   getContainer: function(terminalName){
   	
	for(var i=0;i<this.layer.containers.length;i++){
		if(this.layer.containers[i].terminals[0].options.name == terminalName.options.name)
			return this.layer.containers[i];
	}
	
		return '';
   }

};

})();
