/**
 * @file Attaches Javascript behaviors to various elements of the NIEER 
 *   Yearbook database.  Each component on the page has a default behavior if
 *   Javascript is disabled, but Javascript provides a much richer and faster
 *   experience.
 *
 * Copyright 2010 - New Signature
 *
 * @author Andrew Marcus
 * @author Seamus Leahy
 */

(function($) {

  var selectorProgramHeight = 300;
  var selectorProgramMaxHeight = 300;
  var selectorFieldHeight = 400;
  var selectorFieldMaxHeight = 400;
  var headerHeight = 113;
  var toolbarHeight = 32;
  var animateTime = 200;
  var showing = false;
  var forms;
  var buttons;
  var toggles = {};
  var table;
  var hover;
  var hovering;

  function resetHover() {
    hover = {
      'row' : null,
      'col' : null,
      'field' : null,
      'program' : null,
      'rowhead' : null,
      'colhead' : null
    };
  }

  function init() {
    resetHover();
    
    headerHeight = $('#header').height();
    
    // Bind the toolbar widgets to use AJAX
    forms = $('#form');
    bindToolbar($('#toolbar'));

    // Bind the selector widgets to use AJAX
    bindSelector($('#programs-selector').data('page', 'programs'));
    bindSelector($('#fields-selector').data('page', 'fields'));

    // Bind the table sort columns to use AJAX
    table = $('#table');
    bindTableHeaders(table);
    bindTable(table);
    
    // Bind the window scroll and resize events
    bindWindow();

    // Setup the CSS for animate in and out
    $('#programs-selector.hide, #fields-selector.hide').css('opacity', 0);
  }

  /**
   * All toggle links in the toolbar should animate the expand/collapse action
   * and use AJAX to report the current state.
   */
  function bindToolbar(toolbar) {
    toggles = {};
    toolbar.find('h3.form-expander').each(function() {
      var toggle = $(this);

      // Get the base URL of the link
      var a = toggle.find('a');
      var url = a.attr('href');
      url = url.replace('&show=0', '');
      url = url.replace('&show=1', '');

      // Lookup the associated form
      var page = toggle.attr('expands');
      var form = forms.find('#' + page + '-selector');
      if (form.length) {
        toggles[page] = toggle;

        // Attach a show/hide action to each of the forms
        toggle.bind('rolldown', function(event, callback) {

          // If a different form is already showing, roll it up first
          if (showing && showing != toggle) {
            showing.trigger('rollup', [ function() {
              toggle.trigger('rolldown');
            } ]);
          } 
          else {
            showing = toggle;
            toggle.addClass('expanded');

            // Reload the page through AJAX first
            $.get(url, { 'page' : page, 'show' : 1 }, function(results) {
              // Replace the HTML and rebind the widget
              bindSelector(form, results);

              // Slide down the pane
              form.queue(function() {
                $(this).css('display', 'block');
                $(this).dequeue();
              }).animate({ opacity : 1 }, animateTime, callback);
              form.removeClass('hide');
            });
          }
          return false;
        });

        toggle.bind('rollup', function(event, callback) {
          showing = false;
          toggle.removeClass('expanded');
          form.animate({ opacity : 0 }, animateTime, function() {
            $(this).css('display', 'none');
            if ($.isFunction(callback)) {
              callback();
            }
          });
          form.addClass('hide');

          // Let the server know the form has closed.
            $.get(url, { 'page' : 'none', 'show' : 0 });
            return false;
          });

        // Initialize with the currently-showing form
        if (!form.hasClass('hide')) {
          showing = toggle;
        }

        // When the link is clicked, do the associated action
        a.click(function() {
          // If the form is hidden, show it.
          if (form.hasClass('hide')) {
            toggle.trigger('rolldown');
          }
          // If the form is showing, hide it.
          else {
            toggle.trigger('rollup');
          }
          return false;
        });
        toggle.click(function() {
          a.click();
          return false;
        });
      }
    });
  }

  /**
   * All links in the selector widgets should reload via AJAX instead.
   */
  function bindSelector(selector, html) {
    if (html) {
      selector.html(html);
    }

    // Attach to each of the list elements in this selector
    selector.find('ul').each(function(i) {
      var ul = $(this);

      ul.find('li').each(function() {
        var li = $(this);

        // Rebind all the links to load through AJAX
        var a = li.find('a').click(function() {
          var a = $(this);
          var url = a.attr('href');

          // Save the current scroll location
          var scroll = ul.scrollTop();

          $.get(url, { 'page' : selector.data('page') }, function(results) {
            // Replace the HTML and rebind the widget
            bindSelector(selector, results);

            // Restore the scroll position
            selector.find('ul').eq(i).scrollTop(scroll);
          }, 'text');
          return false;
        });

        // Clicking the row performs the behavior of the first link
        li.click(function() {
          a.eq(0).click();
          return false;
        });

        // Clicking the checkbox performs the behavior of the first link
        li.find(':checkbox').removeAttr('onclick').click(function() {
          li.click();
          return false;
        });

        // Don't allow clicks on any disabled links
        selector.find('.submit-buttons a.disabled').click(function() {
          return false;
        });
      });

      // Set the height of the selectors
      $('#fields-selector .selector ul').height(selectorFieldHeight);
      $('#programs-selector ul').height(selectorProgramHeight);
    });
  }

  /**
   * All sorting links in the table should reload via AJAX instead.
   */
  function bindTable(table, html) {
    if (table.length == 0) {
      return false;
    }

    if (html) {
      table.html(html);
    }

    // Bind sortable column headers to refresh the page via AJAX
    table.find("th a").each(function() {
      var a = $(this);
      var cell = a.parents('th');
      var url = a.attr('href');
      a.click(function() {
        $.get(url, {
          'page' : 'table'
        }, function(results) {
          // Replace the HTML and rebind the widget
          bindTable(table, results);
        });
        return false;
      });
      cell.click(function() {
        a.click();
        return false;
      });
    });

    // Find the cell the mouse is currently over
    if (hover.field && hover.program) {
      var cell = table.find('tbody tr[program_id=' + hover.program + '] td[field_id=' + hover.field + ']');
    } 
    else if (hover.program) {
      var cell = table.find('tbody tr[program_id=' + hover.program + '] th');
    }
    else if (hover.field) {
      var cell = table.find('thead th[field_id=' + hover.field + ']');
    }

    // If a cell is selected, trigger a mousemove event to restore the selection
    hover.field = null;
    hover.program = null;

    if (cell) {
      cell.mousemove();
    }
  }

  /**
   * Highlights the selected row and column, and creates floating headers to
   * indicate where the user is pointing in a large table.
   */
  function bindTableHeaders(table) {
    // Bind the table headers
    hovering = false;

    table.mousemove(function(event) {
      // Don't let this happen more than once at a time
      if (hovering) {
        return true;
      }
      hovering = true;
      var cell = $(event.target);

      // Columns
      var field = cell.attr('field_id');
      var col = null;
      if (hover.field != field) {
        if (hover.col) {
          hover.col.removeClass('col-hover');
        }
        if (hover.colhead) {
          hover.colhead.remove();
          hover.colhead = false;
        }

        if (field) {
          var col = table.find('[field_id=' + field + ']');
          col.addClass('col-hover');

          // Create stationary col headers
          var h = col.filter('th');
          if (h.get(0) != cell.get(0)) {
            hover.colhead = h.clone(true)
            .data('original', h)
            .attr('id', 'col-float')
            .addClass('header-float')
            .css({
              'position' : 'absolute',
              'width' : h.width(),
              'height' : h.height(),
              'z-index' : 999
            }).appendTo($('body'));
          }
        }
        hover.col = col;
        hover.field = field;
      }

      // Rows
      var row = cell.parents('tr');
      var program = row.attr('program_id');
      if (hover.program != program) {
        if (hover.row) {
          hover.row.removeClass('hover');
        }
        if (hover.rowhead) {
          hover.rowhead.remove();
          hover.rowhead = false;
        }

        if (program) {
          row.addClass('hover');

          // Create stationary row headers
          var h = row.find('th');
          if (h.get(0) != cell.get(0)) {
            hover.rowhead = h.clone(true)
            .data('original', h)
            .attr('id', 'row-float')
            .addClass('header-float')
            .css({
              'position' : 'absolute',
              'width' : h.width(),
              'height' : h.height(),
              'z-index' : 999
            }).appendTo($('body'));
          }
        }
        hover.row = row;
        hover.program = program;
      }

      // Trigger a scroll event to show or hide the header
      scrollHeaders();

      hovering = false;
      return true;
    });
  }
  
  /**
   * Repositions the floating table headers based on where the page is scrolled
   */
  function scrollHeaders() {
    if (hover.colhead) {
      if ($(document).scrollTop() <= table.offset()['top'] + toolbarHeight) {
        hover.colhead.hide();
      } else {
        var h = hover.colhead.data('original');
        hover.colhead.css( {
          'top' : $(document).scrollTop() + toolbarHeight,
          'left' : h.offset()['left']
        });
        hover.colhead.show();
      }
    }
    if (hover.rowhead) {
      if ($(document).scrollLeft() <= table.offset()['left']) {
        hover.rowhead.hide();
      } else {
        var h = hover.rowhead.data('original');
        hover.rowhead.css( {
          'top' : h.offset()['top'],
          'left' : $(document).scrollLeft()
        });
        hover.rowhead.show();
      }
    }
  }

  /**
   * Binds to the window scroll and resize events to position elements appropriately.
   */
  function bindWindow() {
    $(window).scroll(function(event) {
      scrollHeaders();

      // Stick the toolbar to the top when scrolling away from the top
      if ($(window).scrollTop() > headerHeight) {
        $('#toolbar').stop().css( {
          position : 'fixed',
          top : 0,
          left : 0
        });
        $('#form').stop().css( {
          position : 'fixed',
          top : 0,
          left : 0
        });
      } else {
        $('#toolbar').css( {
          position : '',
          top : '',
          left : $(window).scrollLeft()
        });
        $('#form').css( {
          position : '',
          top : '',
          left : $(window).scrollLeft()
        });
      }
    });

    // resize the form to fit on the screen.
    $(window).resize( function(event) {
      selectorFieldHeight = Math.max(75, Math.min($(window).height() - 150, selectorFieldMaxHeight));
      $('#fields-selector .selector-column ul').height(selectorFieldHeight);
          
      selectorProgramHeight = Math.max(75, Math.min($(window).height() - 150, selectorProgramMaxHeight));
      $('#programs-selector .selector-column ul').height(selectorProgramHeight);
    }).resize();
  }

  $(document).ready(init);
})(jQuery);
