dojo.require('dojo.event.*');
dojo.require('dojo.dom.*');
dojo.require('dojo.html.*');
dojo.require('dojo.lang.declare');
dojo.require('dojo.lang.func');
dojo.require('dojo.lfx.*');

// navigation class
// declares the UI functionality and
// registers events for the primary navigation

var FADE_IN_TIME = 200
var FADE_OUT_TIME = 400

dojo.declare('PrimaryNavigation', null, {

   // uses the id names of the main navigation div
   // and the content div to create the navigation
   // functionality
   initializer: function(mainNav, content, button) {
      // the nav div
      this.navNode = dojo.byId(mainNav);

      // currently running animations 
      this.hideMainAnim = null;
      this.showMainAnim = null;
      this.hideSubAnim = null;
      this.showSubAnim = null;

      // entire main nav is active if button is not specified
      if (!button) {
         this.contentNode = dojo.byId(content);
         this.currentNode = this.contentNode;         
         this.currentButton = null; 
         this.currentSubNode = null;
         this.currentSubButton = null;
      
         // connect the main button events
         this.connectMainNavEvents();   
      } else {
         
         // get the button that was pressed
         button = dojo.byId(button);
      
         // only the sub nav under this button is active
         this.contentNode = null;
         this.currentNode = this.getSubNav(button);
         this.currentButton = null;
         this.currentSubNode = null;
         this.currentSubButton = null;
         
         // now, we need to add event listeners
         this.connectSubNavEvents(this.currentNode);
         dojo.event.connect(button, 'onmousemove', this, 'onFixedNavButton');

         // set up the main nav button description
         display = this.getMainNavDisplay(this.getSubNav(button));
         this.showSubNode(display);
         this.currentSubNode = display;
      }
   },

   ////////////////////////////////////////////////////////////////////////////
   // DOM utilities
   ////////////////////////////////////////////////////////////////////////////
   
   // get the main nav node containing all of the buttons
   getMainNav: function() {
      return dojo.dom.getFirstChildElement(this.navNode);  
   },
   
   // get the next main nav button (from the given button; use null for first button)
   getNextMainNavButton: function(navButton) {
      if (navButton == null)
         return dojo.dom.getFirstChildElement(this.getMainNav());
      else
         return dojo.dom.getNextSiblingElement(navButton);
   },
   
   // get the main nav button display element
   getMainNavDisplay: function(subNav) {
      return dojo.dom.getNextSiblingElement(subNav);
   },
   
   // get the sub nav node from a main nav button
   getSubNav: function(button) {
      sibling = dojo.dom.getFirstChildElement(button);
      return dojo.dom.getNextSiblingElement(sibling);
   },
   
   // get the next sub nav button from the given button;
   // use null for first button and pass in the mainButton under
   // which the sub nav button lives
   getNextSubNavButton: function(subNavButton, subNav) {
      if (subNavButton == null) {
         return dojo.dom.getFirstChildElement(subNav);
      } else {
         return dojo.dom.getNextSiblingElement(subNavButton);
      }
   },
   
   // get the sub navigation button's display
   getSubNavDisplay: function(subNavButton) {
      sibling = dojo.dom.getFirstChildElement(subNavButton);
      return dojo.dom.getNextSiblingElement(sibling);
   },

   ///////////////////////////////////////////////////////////////////////////
   // Event connection and disconnection   
   ///////////////////////////////////////////////////////////////////////////

   // connect main navigation events
   connectMainNavEvents: function() {
      // loop through all main navigation buttons and connect
      // them to the onmousemove event
      navButton = this.getNextMainNavButton(null);
      while (navButton) {
         dojo.event.connect(navButton, 'onmousemove', this, 'onNavButton');
         navButton = this.getNextMainNavButton(navButton);
      }
      // prevent mousemove events from being propagated while
      // the mouse is inside the navigation boundaries
      dojo.event.connect(this.getMainNav(), 'onmousemove', this, 'onNav');
      // handle all mousemove events outside of the navigation
      dojo.event.connect(document, 'onmousemove', this, 'onOutsideNav');       
   },
   
   // don't have to disconnect main navigation events
   
   // connect sub navigation events
   connectSubNavEvents: function(node) {
      // we need to connect event listeners for all of the
      // subnav elements
      subNavButton = this.getNextSubNavButton(null, node);
      while (subNavButton) {
         // connect the event
         dojo.event.connect(subNavButton, 'onmousemove', this, 'onSubNavButton');
         // move on to the next subnavigation button         
         subNavButton = this.getNextSubNavButton(subNavButton);
      }
   },
   
   // disconnect sub navigation events() 
   disconnectSubNavEvents: function(node) {
      // we need to disconnect all of the node's 
      // sub navigation event listeners
      subNavButton = this.getNextSubNavButton(null, node);
      while (subNavButton) {
         // disconnectconnect the event
         dojo.event.disconnect(subNavButton, 'onmousemove', this, 'onSubNavButton');
         // move on to the next subnavigation button
         subNavButton = this.getNextSubNavButton(subNavButton)
      }
   },

   ///////////////////////////////////////////////////////////////////////////
   // Animation completion callbacks
   ///////////////////////////////////////////////////////////////////////////

   // complete the animations by setting the corresponding variable to null
   hideMainAnimDone: function() {
      this.hideMainAnim = null;
   },
   showMainAnimDone: function() {
      this.showMainAnim = null;
   },
   hideSubAnimDone: function() {
      this.hideSubAnim = null;
   },
   showSubAnimDone: function() {
      this.showSubAnim = null;
   },
   
   ///////////////////////////////////////////////////////////////////////////
   // Highglight navigation buttons
   ///////////////////////////////////////////////////////////////////////////
   
   // highlight main nav button
   highlightMainNavButton: function(button) {
      if (this.currentButton)
         dojo.html.removeClass(this.currentButton, "selected");
      dojo.html.addClass(button, "selected");
      this.currentButton = button;
   },
   // darken main nav button
   darkenMainNavButton: function(button) {
      dojo.html.removeClass(button, "selected");
      this.currentButton = null;
   },
   // highlight sub nav button
   highlightSubNavButton: function(button) {
      if (this.currentSubButton)
         dojo.html.removeClass(this.currentSubButton, "selected");
      dojo.html.addClass(button, "selected");
      this.currentSubButton = button;
   },
   // darken sub nav button
   darkenSubNavButton: function(button) {
      dojo.html.removeClass(button, "selected");
      this.currentSubButton = null;
   },

   ///////////////////////////////////////////////////////////////////////////
   // Show and hide navigation menus 
   ///////////////////////////////////////////////////////////////////////////

   // hide the given main nav button
   hideMainNav: function(node, doPlay) {
      if (doPlay === undefined)
         doPlay = true;
      if (this.hideMainAnim != null)
         this.hideMainAnim.gotoPercent(100, true);
      this.hideMainAnim = dojo.lfx.fadeHide(node, FADE_OUT_TIME, null, dojo.lang.hitch(this, 'hideMainAnimDone'));
      if (doPlay)
         this.hideMainAnim.play();
      return this.hideMainAnim;
   },
   
   // show the given main nav button
   showMainNav: function(node, doPlay) {
      if (doPlay === undefined)
         doPlay = true;
      if (this.showMainAnim != null)
         this.showMainAnim.gotoPercent(100, true);
      this.showMainAnim = dojo.lfx.fadeShow(node, FADE_IN_TIME, null, dojo.lang.hitch(this, 'showMainAnimDone'));
      if (doPlay)
         this.showMainAnim.play();
      return this.showMainAnim;
   },

   // hide the given sub nav button
   hideSubNav: function(subNode, doPlay) {
      if (doPlay === undefined)
         doPlay = true;
      if (this.hideSubAnim != null)
         this.hideSubAnim.gotoPercent(100, true);
      this.hideSubAnim = dojo.lfx.fadeHide(subNode, FADE_OUT_TIME, null, dojo.lang.hitch(this, 'hideSubAnimDone'));
      if (doPlay)
         this.hideSubAnim.play();
      return this.hideSubAnim;
   },
   
   // show the given sub nav button
   showSubNav: function(subNode, doPlay) {
      if (doPlay === undefined)
         doPlay = true;
      if (this.showSubAnim != null)
         this.showSubAnim.gotoPercent(100, true);   
      this.showSubAnim = dojo.lfx.fadeShow(subNode, FADE_IN_TIME, null, dojo.lang.hitch(this, 'showSubAnimDone'));
      if (doPlay)
         this.showSubAnim.play();
      return this.showSubAnim;
   },
   
   // swap the given node in as the current navigation sub menu node
   // and hide the previously displayed node
   showNode: function(node) {
      // only process this if we are actually switching nodes
      if (this.currentNode != node) {
         // if we're going to display the content,
         // hide the sub navigation
         if (node == this.contentNode) {
            // hide the main and sub button selection highlighting
            // and animate hiding of the sub navigation button's contents
            if (this.currentButton != null)
               this.darkenMainNavButton(this.currentButton);
            if (this.currentSubButton != null)
               this.darkenSubNavButton(this.currentSubButton);
            if (this.currentSubNode != null) {
               this.hideSubNav(this.currentSubNode);
               this.currentSubNode = null;
            }
         } else {
            // special case for sub navigation nodes
            // node != this.contentNode
            
            // is the current node the contentNode or not?
            if (this.currentNode != this.contentNode) 
               // we need to disconnect all of the
               // event listeners inside this node
               this.disconnectSubNavEvents(this.currentNode)
            
            // now, we need to add event listeners on the new nodes
            this.connectSubNavEvents(node);

            // set up the main nav button description
            display = this.getMainNavDisplay(node);
            this.showSubNode(display);
            this.currentSubNode = display;
         }
         
         // set up: swap the nodes with a cross-fade animation         
         anim_hide = this.hideMainNav(this.currentNode, false);
         anim_show = this.showMainNav(node, false);
         anim = dojo.lfx.combine(anim_hide, anim_show);

         // set the current node and play the animation
         this.currentNode = node;
         anim.play();
      }
   },
   
   // swap the given sub node display in as the current display node
   // and hide the previously displayed node
   showSubNode: function(node) {
      // fade in or crossfade, as appropriate
      if (this.currentSubNode != null) {
         // set up: swap the nodes with a cross-fade animation         
         anim_hide = this.hideSubNav(this.currentSubNode, false);
         anim_show = this.showSubNav(node, false);
         anim = dojo.lfx.combine(anim_hide, anim_show);                  
      } else {
         // set up: show the node
         anim = this.showSubNav(node, false);  
      }
      
      // set the current sub node and play the animation
      this.currentSubNode = node;
      anim.play();
   },

   // called when the mouse is anywhere in the navigation
   // when a state change should not occur.
   onNav: function(evt) {
      // don't let the body get notified
      evt.stopPropagation();
   },

   // called when the mouse is on a main nav button
   onNavButton: function(evt) {
      // the current target here is a main nav button
      button = evt.currentTarget

      // prevent parents from processing this event
      evt.stopPropagation();

      // if this button is already being hovered over, we
      // may simply need to change the display text
      if (this.currentButton == button) {
      
         // set up the main nav button description
         display = this.getMainNavDisplay(this.getSubNav(button));
         if (display != this.currentSubNode) {
            this.showSubNode(display);
            this.currentSubNode = display;
            
            // we need to darken the subbutton
            if (this.currentSubButton != null)
               this.darkenSubNavButton(this.currentSubButton);            
         }
         return;
      }
      
      // we need to highlight the button
      if (this.currentButton != null)
         this.darkenMainNavButton(this.currentButton);
      this.highlightMainNavButton(button);

      // display the sub navigation for this button
      this.showNode(this.getSubNav(button));
   },
   
   onFixedNavButton: function(evt) {
      // the current target here is a main nav button
      button = evt.currentTarget
      
      // prevent parents from processing this event
      evt.stopPropagation();
      
      // set up the main nav button description
      display = this.getMainNavDisplay(this.getSubNav(button));
      if (display != this.currentSubNode) {
         this.showSubNode(display);
         this.currentSubNode = display;
         
         // we need to darken the subbutton
         if (this.currentSubButton != null)
            this.darkenSubNavButton(this.currentSubButton);            
      }  
   },
   
   // called when the mouse is on a sub-nav button
   onSubNavButton: function(evt) {
      // the current target here is an li tag
      button = evt.currentTarget;
      
      // prevent parents from processing this event
      evt.stopPropagation();
      
      // if this button is already being hovered over, don't do anything
      if (this.currentSubButton == button)
         return;

      // we need to highlight the button by setting the selected class
      if (this.currentSubButton != null)
         this.darkenSubNavButton(this.currentSubButton);
      this.highlightSubNavButton(button);
         
      // get the button's display
      display = this.getSubNavDisplay(button);
      
      // display the changed display node
      this.showSubNode(display);
   },
   
   onOutsideNav: function() {
      this.showNode(this.contentNode);
   }
});


