/**
 *  Manager for tabs and their content panels.
 *
 *  Requires:
 *      JsEx.js
 *      JsUtils.js
 *
 *  Usage:
 *      var manager = new JsTabsManager();
 *      manager.registerTab('browse', 'browse-tab', 'browse-panel');
 *      manager.registerTab('search', 'search-tab', 'search-panel');
 *      manager.showPanel('browse');
 *
 *  @author achou
 */
function JsTabsManager()
{
    ////////////////////////////////////////////////////////////////////////////
    //
    //  Fields.

    var _this = this;
    var _tabs = null;
    var _currentTab = null;
    var _listeners = null;


    ////////////////////////////////////////////////////////////////////////////
    //
    //  Constructor.

    /**
     *  Initializes a new tabs manager.
     */
    var _ctor = function()
        {
            _tabs = {};
            _listeners = [];
        };


    ////////////////////////////////////////////////////////////////////////////
    //
    //  Public methods.

    /**
     */
    this.getCurrentTab = function()
        {
            return _currentTab;
        };

    /**
     */
    this.getTab = function(name)
        {
            return _tabs[name];
        };

    /**
     */
    this.addListener = function(listener)
        {
            if (listener.tabChanged)
                _listeners.push(listener);
        };

    /**
     *  Registers a tab for management.  The tab's panel will be shown when it's
     *  clicked and hidden when any other tab is clicked.
     *
     *  @param  tabName (Any arbitrary) name for the tab.
     *  @param  tabIndex TODO
     *  @param  tabId ID of the HTML <a href="..."> node that represents the
     *          tab.
     *  @param  panelId ID of the content panel associated with the tab.
     */
    this.registerTab = function(tabName, tabIndex, tabId, panelId)
        {
            var tabNode = dojo.byId(tabId);
            var panelNode = dojo.byId(panelId);
            var tab = null;
            if (tabNode && panelNode)
            {
                tab = new JsTab(_this, tabName, tabIndex, tabNode, panelNode);
                _tabs[tabName] = tab;
                dojo.connect(tabNode, 'onclick', tab,
                             function(event) {
                                 this.showPanel();
                                 // Get rid of the "active" outline.
                                 this.getTab().blur();
                                 dojo.stopEvent(event);
                             });
            }
            return tab;
        };

    /**
     *  Displays the content panel associated with a tab.
     *
     *  @param  tabName Name of the tab.
     */
    this.showPanel = function(tabName)
        {
            var oldTab = _currentTab;
            _currentTab = _tabs[tabName];
            for (var name in _tabs)
            {
                var tab = _tabs[name].getTab();
                var panel = _tabs[name].getPanel();
                if (name == tabName)
                {
                    HtmlUtils.showElement(panel);
                    HtmlUtils.addClass(tab, 'selectedTab');
                }
                else
                {
                    HtmlUtils.hideElement(panel);
                    HtmlUtils.removeClass(tab, 'selectedTab');
                }
            }

            for (var i = 0, n = _listeners.length; i < n; i++)
            {
                _listeners[i].tabChanged.call(_this, oldTab, _currentTab);
            }
        };


    ////////////////////////////////////////////////////////////////////////////
    //
    //  Invoke constructor.

    _ctor();


    ////////////////////////////////////////////////////////////////////////////
    //
    //  Inner classes.

    /**
     *  Data structure that represents a tab and its associated content.
     */
    var JsTab = function(tabsManager, tabName, tabIndex, tabNode, panelNode)
    {
        var _this = this;
        var _tabsManager = null;
        var _tabName = null;
        var _tabIndex = null;
        var _tabNode = null;
        var _panelNode = null;
        var _listeners = null;

        /**
         *  Instantiates the tab.
         *
         *  @param  tabsManager The manager that controls this tab.
         *  @param  tabIndex TODO
         *  @param  tabName (Any arbitrary) name for this tab.
         *  @param  tabNode HTML <a href="..."> node that represents the tab.
         *  @param  panelNode HTML node that is the content panel associated
         *          with this tab.
         */
        var _ctor = function(tabsManager, tabName, tabIndex, tabNode, panelNode)
            {
                _tabsManager = tabsManager;
                _tabName = tabName;
                _tabIndex = tabIndex;
                _tabNode = tabNode;
                _panelNode = panelNode;
                _listeners = [];
                dojo.connect(_tabNode, 'onmouseover', _this, _mouseOver);
                dojo.connect(_tabNode, 'onmouseout', _this, _mouseOut);
            };

        /**
         *  Returns the name of this tab.
         *
         *  @return Name of this tab.
         */
        this.getName = function()
            {
                return _tabName;
            };

        /**
         */
        this.getIndex = function()
            {
                return _tabIndex;
            };

        /**
         *  Returns the HTML <a href="..."> node that represents this tab.
         *
         *  @return HTML node of this tab.
         */
        this.getTab = function()
            {
                return _tabNode;
            };

        /**
         *  Returns the HTML code that represents this tab's content panel.
         *
         *  @return Content panel associated with this tab.
         */
        this.getPanel = function()
            {
                return _panelNode;
            };

        /**
         */
        this.addListener = function(listener)
            {
                if (listener.mouseOver && listener.mouseOut)
                    _listeners.push(listener);
            };

        /**
         */
        var _mouseOver = function(event)
            {
                for (var i = 0, n = _listeners.length; i < n; i++)
                {
                    _listeners[i].mouseOver.call(_this, _this, event);
                }
            };

        /**
         */
        var _mouseOut = function(event)
            {
                for (var i = 0, n = _listeners.length; i < n; i++)
                {
                    _listeners[i].mouseOut.call(_this, _this, event);
                }
            };

        /**
         *  Calls JsTabsManager.showPanel to display the content panel
         *  associated with this tab.
         */
        this.showPanel = function()
            {
                var currentTab = _tabsManager.getCurrentTab();
                if ((currentTab == null) || (_tabIndex < currentTab.getIndex()))
                    _tabsManager.showPanel(_tabName);
            };

        // Invoke the constructor.
        _ctor(tabsManager, tabName, tabIndex, tabNode, panelNode);
    }
}
