SelectableManager = Class.create();

SelectableManager.prototype = {

   initialize: function() {
      this.selectables = new Array();
      this.selectElement = null;
      this.currentSelection = null;
      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
   },

   reset: function() {
   	if (null != this.currentSelection) {
      	this.currentSelection.deselect();
      	this.currentSelection = null;
      }
      this.resetSelectables();
      this.selectElement = null;
      //TODO do we need to remove event handlers
   },

   registerSelectable: function( aSelectable ) {
      this.selectables[ this.selectables.length ] = aSelectable;
      this._addMouseDownHandler( aSelectable );
   },

   resetSelectables: function() {
      for ( var i = 0 ; i < this.selectables.length ; i++ ) {
         this._removeMouseDownHandler( this.selectables[i] );
      }
      this.selectables = new Array();
   },

   deregisterSelectable: function( aSelectable ) {
      var newSelectables = new Array();
      var j = 0;
      for ( var i = 0 ; i < this.selectables.length ; i++ ) {
         if ( this.selectables[i] != aSelectable ) {
            newSelectables[j++] = this.selectables[i];
         } else {
            this._removeMouseDownHandler( aSelectable );
         }
      }
      this.selectables = newSelectables;
   },

   clearSelection: function() {
   	if (null != this.currentSelection) {
      	this.currentSelection.deselect();
      	this.currentSelection = null;
      }
   },

   hasSelection: function() {
      return (null == this.currentSelection);
   },

   updateSelection: function( selectable, extendSelection ) {
      if ( ! extendSelection )
         this.clearSelection();

      if ( selectable.isSelected() ) {
         selectable.deselect();
         if ( selectable == this.currentSelection )
            this.currentSelection = null;
      }
      else {
         this.currentSelection = selectable;
         selectable.select();
      }
   },

   _mouseDownHandler: function(e) {
      if ( arguments.length == 0 )
         e = event;

      // if not button 1 ignore it...
      var nsEvent = e.which != undefined;
      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
         return;

      var eventTarget      = e.target ? e.target : e.srcElement;
      var selectableObject  = eventTarget.selectable;

      var candidate = eventTarget;
      while (selectableObject == null && candidate.parentNode) {
         candidate = candidate.parentNode;
         selectableObject = candidate.selectable;
      }
   
      if ( selectableObject == null )
         return;

      //this.updateSelection( selectableObject, e.ctrlKey );
      //TODO if we want multiselect we need to implement
      this.updateSelection( selectableObject, false );

   },


   _mouseUpHandler: function(e) {
      if ( ! this.hasSelection() )
         return;

      var nsEvent = e.which != undefined;
      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
         return;

      if ( this.selectElement == null ) {
         this._terminateEvent(e);
         return;
      }

      Event.stopObserving(document.body, "mouseup",  this._mouseUp);
   },

   _addMouseDownHandler: function( aSelectable )
   {
      htmlElement  = aSelectable.getMouseDownHTMLElement();
      if ( htmlElement  != null ) { 
         htmlElement.selectable = aSelectable;
         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
         Event.observe(htmlElement, "mousedown", this._mouseDown);
      }
   },

   _removeMouseDownHandler: function( aSelectable )
   {
      htmlElement  = aSelectable.getMouseDownHTMLElement();
      if ( htmlElement  != null ) { 
         htmlElement.selectable = null;
         Event.stopObserving(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
         Event.stopObserving(htmlElement, "mousedown", this._mouseDown);
      }
   },

   _onmousedown: function () {
     Event.observe(document.body, "mouseup",  this._mouseUp);
   },

   _terminateEvent: function(e) {
      if ( e.stopPropagation != undefined )
         e.stopPropagation();
      else if ( e.cancelBubble != undefined )
         e.cancelBubble = true;

      if ( e.preventDefault != undefined )
         e.preventDefault();
      else
         e.returnValue = false;
   },


   initializeEventHandlers: function() {
      if ( typeof document.implementation != "undefined" &&
         document.implementation.hasFeature("HTML",   "1.0") &&
         document.implementation.hasFeature("Events", "2.0") &&
         document.implementation.hasFeature("CSS",    "2.0") ) {
         document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
      }
      else {
         document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
      }
   }
}

var selectMgr = new SelectableManager();
selectMgr.initializeEventHandlers();


Selectable = Class.create();

Selectable.prototype = {

   initialize: function( type, htmlElement, callback ) {
      this.type          = type;
      this.htmlElement   = $(htmlElement);
      this.selected      = false;
      this.callback      = callback;
   },

   /**
    *   Returns the HTML element that should have a mouse down event
    *   added to it in order to initiate a select operation
    *
    **/
   getMouseDownHTMLElement: function() {
      return this.htmlElement;
   },

   select: function() {
      //this.log("start select");
      this.selected = true;

      if ( this.showingSelected )
         return;
      //this.log("select 1");

      var htmlElement = this.getMouseDownHTMLElement();
      //this.log("select 2");

		this.onSelect(htmlElement);
      this.showingSelected = true;
      //this.log("select end");
   },

   deselect: function() {
      //this.log("start deselect");
      this.selected = false;
      if ( !this.showingSelected )
         return;

      var htmlElement = this.getMouseDownHTMLElement();

      htmlElement.style.backgroundColor = this.saveBackground;
      this.showingSelected = false;
   },

   isSelected: function() {
      return this.selected;
   },

   toString: function() {
      return this.type + ":" + this.htmlElement + ":";
   },

	onSelect: function(htmlElement) {
      var color = Rico.Color.createColorFromBackground(htmlElement);
      color.isBright() ? color.darken(0.333) : color.brighten(0.333);
	   this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
   	htmlElement.style.backgroundColor = color.asHex();
	},


   log: function(str) {
      new Insertion.Bottom( $('logger'), "<span class='logMsg'>" + str + "</span>" );
      $('logger').scrollTop = $('logger').lastChild.offsetTop;

   }
}

