//------------------------------------------------------------------------------
// EventSource is a mixin for classes that need even dispatching capability
// listeners register for a particular event and can include an alternate handler name
var EventSource = {
  getListenerList:function() {
    if( this.listenerList == null ) {
      this.listenerList = {};
    }
    return this.listenerList;
  },

  getListeners:function(t) { 
    var list = this.getListenerList()[t];
    if (list != null){
      return list;
    }
    return null;   
  },
  
  observe: function( t, h ){
    var list = this.getListeners( t );
    if (list == null){
      list = [];
      this.listenerList[t] = list;
    }
    
    list.push( { handler:h } );
    return this;
  },
  
  stopObserving: function( t, h ){
    var list = this.getListeners( t );
    if (list != null){
      var tmp = [];
      for( var i=0, j=0, l=list.length; i<l; i++ ){
        var li = list[i];
        if( li.handler != h ){
          tmp[j] = li;
          j++;
        }
      }
      
      this.listenerList[t] = tmp;
    }
    return this;
  },
  
  fireEvent: function( t, e ) {
    e = e || {};
    e.source = this;
    e.type = t;
    
    var list = this.getListeners( t );
    if( list != null ) {
      // make a copy
      var list = [].concat( list );
      for( var i=0, l=list.length; i<l; i++ ){
				list[i]['handler'](e);
        //list[i].target[ list[i].handler || t ](e);
      }
    }
  }
}


var ComponentBase = Class.create({
  initialize:function( e ){
      this.id = e.id;
      this.e = e;
      this.installUI();
  },

  installUI: function() {
    // over ride in sub class
  },
  
  observe: function( type, handler ) {
    this.e.observe( type, handler.bindAsEventListener( this ) );
  },
  
  insert: function( obj ) {
    var e = Object.isElement( obj ) ? obj : obj.e;
    this.e.insert( e )
    return obj;
  },
  
  remove: function( obj ) {
    var e = Object.isElement( obj ) ? obj : obj ? obj.e : this.e;
    e.remove();
    return obj;
  },
  
  clear: function() {
    this.e.update();
  },

  update: function( content ) {
    this.e.update( content );
    return this;
  },
  
  visible: function() {
    return this.e.visible();
  },
  
  setVisible: function( b ) {
    this[ b ? 'show' : 'hide' ]();
  },
  
  hide: function() {
    this.e.hide()
  },
  
  show: function() {
    this.e.show();
  },
  
  toggleVisibility: function() {
    this.e.toggle();
  },
  
  setOpacity: function( o ) {
    this.e.setOpacity( o );
  },
  
  setLoc: function( point ) {
    this.setX( point.x );
    this.setY( point.y );
  },
  
  getOffset: function() {
    return this.e.positionedOffset();
  },
  
  setX: function( x ) {
    this.setLeft( x );
  },
  
  setY: function( y ) {
    this.setTop( y );
  },
  
  setLeft:function( v ){
    if(isNaN(v) || (v < 0) || (v == Infinity))
      v = 0;
    
    this.e.style.left = v + 'px';
  },

  setTop:function( v ){
    if(isNaN(v) || (v < 0) || (v == Infinity))
      v = 0;
    
    this.e.style.top = v + 'px';
  },

  setSize: function( dim ) {
    this.setWidth( dim.w );
    this.setHeight( dim.h );
  },
  
  setWidth:function( v ) {
    if(isNaN(v) || (v < 0) || (v == Infinity))
      v = 0;
    
    this.e.style.width = v + 'px';
  },

  getWidth:function(){
    return this.e.getWidth();
  },
  
  setHeight:function( v ) {
    if(isNaN(v) || (v < 0) || (v == Infinity))
      v = 0;
    
    this.e.style.height = v + 'px';
  },
  
  getHeight:function(){
    return this.e.getHeight();
  }
});

//------------------------------------------------------------------------------

var StaticUIComponent = Class.create( ComponentBase, {
  initialize:function( $super, id ){
      $super( $( id ) );
  }
});

//------------------------------------------------------------------------------
var LightComponent = Class.create( ComponentBase, {
  initialize:function( $super, options ){
    var options = options || { };
    options.elementType = options.elementType || 'div';
    if( options.classNames ){
      options.className = options.className || '';
      options.className += ' '+options.classNames.join();
    }
    
    this.toolTip = options.toolTip || null;

    $super( $( new Element( options.elementType, options ) ) );
    if( this.toolTip ){
      var that = this;
      this.e.observe( 'mouseover', LightComponent.Tooltip.show.bind( LightComponent.Tooltip, that ) );
      this.e.observe( 'mouseout',  LightComponent.Tooltip.hide.bind( LightComponent.Tooltip, that ) );
    }
  }
});

LightComponent.Tooltip = {
  show: function( c ) {
    if( c.isEnabled && c.isEnabled() == false )
      return;
      
    if( !this.e )
     this.createElement();
    
    var pos = c.e.cumulativeOffset();
    var dim = c.e.getDimensions();
    
    this.label.style.position = 'relative';
    this.label.update( c.toolTip );
    
    var w = this.e.getWidth();
    this.label.style.position = 'absolute';
    var x = Math.min( Math.max( 0, pos.left + dim.width - w ), $( document.body ).getWidth() - w );

    var y = pos.top < 30 ? pos.top + dim.height + 3 : pos.top - 30;
    var arrowx = pos.left - x;
    var arrowy = pos.top < 30 ? 0 : 21;
    var labely = pos.top < 30 ? 6 : 0;
    
    this.e.style.left = x+'px';
    this.e.style.top = y+'px';
    this.arrow.style.left = arrowx+'px';
    this.arrow.style.top = arrowy+'px';
    this.label.style.top = labely+'px';
    
    this.e.className = pos.top < 30 ? 'up' : 'down';
    this.e.show();
  },
  
  hide: function( c ) {
    if( !this.e )
      return;
    this.e.hide();
  },
  
  createElement: function( c ) {
    var e = new Element( 'div', { id:'tooltip' } );
    
    this.label  = new Element( 'label' );
    this.arrow = new Element( 'div', { className:'arrow' });
    
    e.insert( this.label );
    e.insert( this.arrow );
    
    e.hide();
    
    $( document.body ).insert( e );
    this.e = e;
  }
}

//------------------------------------------------------------------------------
var ControlModel = {
  isEnabled: function() {
    return !this.e.hasClassName( 'dsbl' );
  },
  
  setEnabled: function( b ) {
    this[ b ? 'enable' : 'disable' ]();
  },
  
  enable: function() {
    if( !this.isEnabled() )
      this.e.removeClassName( 'dsbl' );
  },
  
  disable: function() {
    if( this.isEnabled() )
      this.e.addClassName( 'dsbl' );
  }
};

//------------------------------------------------------------------------------
var ToggleModel = {
  toggle: function() {
    this.e.toggleClassName( 'on' );
  },
  
  toggleOn: function() {
    if( !this.isSelected() )
      this.e.addClassName( 'on' );
  },
  
  toggleOff: function() {
    if( this.isSelected() )
      this.e.removeClassName( 'on' );
  },
  
  isSelected: function() {
    return this.e.hasClassName( 'on' );
  }
};

//------------------------------------------------------------------------------
var Button = Class.create( LightComponent, ControlModel, EventSource, {
  initialize:function( $super, options ){
    var options = options || { };
    options.elementType = options.elementType || 'a';
    options.className   = options.className || 'button';
    if( options.elementType == 'a' )
      options.href = options.href || '#';
    
    this.label = options.label || 'x';
    $super( options );
    this.e.observe( 'click', this.onClick.bindAsEventListener( this ) );
  },
  
  setLabel: function( text ) {
    this.label.update( text );
  },
  
  getLabel: function( text ) {
    this.label.innerHTML;
  },
  
  onClick: function( e ) {
    if( this.isEnabled() ) {
      Event.stop( e );
      this.fireEvent( 'actionPerformed' );
    }
  },
  
  installUI: function( $super ) { 
    this.label = this.insert( new Element( 'label' ).update( this.label ) );
  }
});

//------------------------------------------------------------------------------
var ToggleButton = Class.create( Button, ToggleModel, {
  initialize:function( $super, options ){
    $super( options );
  },
  
  onClick: function( e ) {
    if( this.isEnabled() ) {
      Event.stop( e );
      this.toggle();
      this.fireEvent( 'actionPerformed' );
    }
  }
});

//------------------------------------------------------------------------------
var RadioButton = Class.create( Button, ToggleModel, {
  initialize:function( $super, options ){
    $super( options );
    this.group = null;
  },
  
  setGroup: function( g ){
    this.group = g;
    this.addListener( 'actionPerformed', g );
  },
  
  onClick: function( e ) {
    if( this.isEnabled() && !this.isSelected() ) {
      Event.stop( e );
      this.fireEvent( 'actionPerformed' );
    }
  }
});

//------------------------------------------------------------------------------
var IconToggle = Class.create( ToggleButton, {
  initialize:function( $super, name, options ){
    var options = options || { };
    options.className   = options.className || 'icon_button';
    options.className += ' '+name
    $super( options );
  }
});

//------------------------------------------------------------------------------
var IconButton = Class.create( RadioButton, {
  initialize:function( $super, name, options ){
    var options = options || { };
    options.className   = options.className || 'icon_button';
    options.className += ' '+name
    $super( options );
  },
  
  stopWait: function() {
    this.e.removeClassName( 'wait' );
  },
  
  wait: function() {
    this.e.addClassName( 'wait' );
  },
  
  installUI: function( $super ) {
    $super;
  }
});

var IconToggleButton = Class.create( RadioButton, {
  initialize:function( $super, name, options ){
    var options = options || { };
    options.className   = options.className || 'icon_button';
    options.className += ' '+name
    $super( options );
  },
  
  onClick: function( e ) {
    if( this.isEnabled() ) {
      Event.stop( e );
      this.toggle();
      this.fireEvent( 'actionPerformed' );
    }
  },
    
  installUI: function( $super ) {
    $super;
  }
});


//------------------------------------------------------------------------------
var SlimButton = Class.create( Button, {
  initialize:function( $super, options ){
    var options = options || { };
    options.className   = options.className || 'button slim';
    $super( options );
  }
});

//------------------------------------------------------------------------------
var MenuButton = Class.create( RadioButton, {
  initialize:function( $super, options ){
    var options = options || { };
    options.className   = options.className || 'button slim';
    $super( options );
  }
});


//------------------------------------------------------------------------------
var MenuManager = Class.create( LightComponent, EventSource, {
  initialize:function( $super, vp ){
    this.viewPort = vp;
    this.menus = {};
    this.activeMenu = null;
    $super( { id: 'menu_manager' } );
    this.e.observe( 'click', this.screenClicked.bindAsEventListener( this ) );
    this.hide();
  },
  
  insertMenu: function( k, m ){
    this.menus[ k ] = m;
    m.setManager( this );
    m.addListener( 'menuDeactivated', this );
    m.addListener( 'menuActivated', this );
    m.hide();
    return this.insert( m );
  },
  
  activate: function( k ) {
    this.show();
  },
  
  deactivate: function( m ) {
    if( this.activeMenu )
      this.activeMenu.deactivate();
    this.hide();
  },

  menuActivated: function( e ) {
    this.activeMenu = e.source;
    this.activate();
  },
  
  menuDeactivated: function() {
    this.hide();
  },
  
  screenClicked: function() {
    this.deactivate();
  }
});

//------------------------------------------------------------------------------
var Menu = Class.create( LightComponent, EventSource, {
  initialize:function( $super, b, options ){
    var options = options || { };
    options.className   = options.className || 'menu';
    this.content = null;
    $super( options );
    
    this.manager = null;
    this.menuItems = [];
    this.activeMenuItem = null;
    this.menuButton = b;
    this.menuButton.addListener( 'actionPerformed', this, 'menuButtonClicked' );
  },
  
  installUI: function( $super ) {
    $super();
    var frame = new LightComponent( { className:'frame' } )
    this.content = frame.insert( new LightComponent( { className:'content' } ) );
    this.insert( frame );
    this.insert( new Element( 'div', { className:'cap_btm' } ) );
  },
  
  setManager: function( m ) {
    this.manager = m;
  },
  
  insertMenuItem: function( mi ){
    this.menuItems[ this.menuItems.length ] = mi;
    mi.addListener( 'actionPerformed', this, 'menuItemClicked' );
    return this.content.insert( mi );
  },
  
  activate: function( k ) {
    var o = this.manager.e.cumulativeOffset();
    var mw = this.manager.getWidth();
    this.setLoc( { x:o.left + mw - 166, y:o.top + 25 } );
    this.show();
    this.menuButton.toggleOn();
    this.fireEvent( 'menuActivated' );
  },
  
  deactivate: function( m ) {
    if( this.activeMenuItem )
      this.activeMenuItem.toggleOff();
    this.hide();
    this.menuButton.toggleOff();
    this.fireEvent( 'menuDeactivated' );
  },
  
  menuButtonClicked: function() {
    this.activate();
  },
  
  menuItemClicked: function() {
    this.deactivate();
    this.fireEvent( 'menuItemClicked' );
  }
});


//------------------------------------------------------------------------------
var Drawers = Class.create( LightComponent, EventSource, {
  initialize:function( $super, options ){
    var options = options || { };
    options.className   = options.className || 'drawers';
    this.content = null;
    this.buttonBar = null;
    this.activeDrawer = null;
    this.drawers = {};
    this.buttons = {};
	this.labels = {};
    
    var ext = { getKey: function( v ) {
      for( var k in this ){
        if( this[ k ] == v ){
          return k;
         }
       }
    } };
    
    Object.extend( this.drawers, ext );
    Object.extend( this.buttons, ext );
    
    $super( options );
  },
  
  installUI: function( $super ) {
    $super();
    var frame = new LightComponent( { className:'frame' } )
    this.content = frame.insert( new LightComponent( { className:'content' } ) );
    this.buttonBar = new LightComponent( { className:'buttons' } );
    
    this.insert( frame );
    this.insert( this.buttonBar );
  },
  
  setManager: function( m ) {
    this.manager = m;
  },
  
  nameToKey: function( n ) {
    return n.toLowerCase().gsub( ' ', '_' ).gsub( '&nbsp;', '_' );
  },
  
  insertDrawer: function( name, content ){
    var k = this.nameToKey( name );
    var button = new ToggleButton( { label:name } );
    
    this.drawers[ k ] = content;
    this.buttons[ k ] = button;
	this.labels[ k ] = name;
    
    button.addListener( 'actionPerformed', this, 'drawerClicked' );
    content.hide();
    
    this.content.insert( content );
    this.buttonBar.insert( button );
    
    return content
  },

  // added this to reset between changing contexts, when there are no comments.
  // probably not neccessary if we are going to hide the buttons instead
  resetButtons: function( buttons ) {
	for ( var i=0, l=buttons.length; i<l; i++) {
		buttons[i].disable();
		buttons[i].setLabel( this.labels[ this.buttons.getKey( buttons[i] ) ] );
	}
  },
  
  drawerClicked: function( e ) {
    var btn = e.source;
    this[ btn.isSelected() ? 'activateDrawer' : 'deactivateDrawer'  ]( this.getDrawer( this.buttons.getKey( btn ) ) );
  },

  getButton: function( k ) {
    return this.buttons[ k ];
  },
  
  getDrawer: function( k ) {
    return this.drawers[ k ];
  },
  
  activateDrawer: function( d ) {
    if( this.activeDrawer == d )
      return;
    
    if( this.activeDrawer )
      this.deactivateDrawer( this.activeDrawer );
    
    this.activeDrawer = d;
    d.show();
    this.buttons[ this.drawers.getKey( d ) ].toggleOn();
  },
  
  deactivateDrawer: function( d ) {
    if( !d )
      return;
      
    if( this.activeDrawer == d )
      this.activeDrawer = null; //should always be the active one anyway?
      
    d.hide();
    this.buttons[ this.drawers.getKey( d ) ].toggleOff();
  }

});

//------------------------------------------------------------------------------
var TabMenu = Class.create( LightComponent, EventSource, {
  initialize:function( $super, options ){
    var options = options || { };
    options.className   = options.className || 'tabs';
	this.defaultTab 	= options.defaultTab || 0;

	
	this.menu = null; // ul
	this.panels = null; // div

    $super( options );
    
    this.tabItems = [];
    this.activeTab = null;
  },
  
  installUI: function( $super ) {
    $super();
    this.menu = new LightComponent( { elementType: 'ul', className:'tab_menu' } );
    this.panels = new LightComponent( { className:'tab_panels' } );

	this.insert( this.menu );
    this.insert( this.panels );
  },
    
  insertTab: function( id, label ){
	
	var l = this.tabItems.length;
	
	var tab = new LightComponent( { elementType: 'li', id: 'tab_menu_'+id, className:'tab_menu_item' } );
	tab.insert( new Element( 'a', { href:'#tab_panel_'+id, rel: 'tab_'+l } ).insert( label ) );
	
 	var panel = new LightComponent( { id: 'tab_panel_'+id, className:'tab_panel clearfix' } );
	panel.hide();
	
    tab.observe( 'click', this.tabClicked.bindAsEventListener( this ) );
	
	this.menu.insert( tab );
	this.panels.insert( panel );
	
    this.tabItems.push( [tab, panel] );

	if ( l == this.defaultTab && this.activeTab == null ) {
		this.activateTab( l );
	}

	return panel; 
  },

  activateTab: function( i ) {
	if ( this.activeTab != null ) {
		this.deactivateTab( this.activeTab );
	}
	
	this.tabItems[ i ][0].e.addClassName('active');
	this.tabItems[ i ][1].show();
	
	this.activeTab = i;
  },

  deactivateTab: function( i ) {
	this.tabItems[ i ][0].e.removeClassName('active');
	this.tabItems[ i ][1].hide();
  },

  resetTabs: function() {
	this.activateTab( this.defaultTab );
  },
  
  tabClicked: function( e ) {
	var i = e.target.readAttribute('rel').split('tab_')[1];
	this.activateTab( i );
    // this.fireEvent( 'tabClicked' );
  }
});

