(function($) {
  Drupal.behaviors.termReferenceTree = {
    attach: function(context, settings) {
      // Bind the term expand/contract button to slide toggle the list underneath.
      $('.term-reference-tree-button', context).click(function() {
        $(this).toggleClass('term-reference-tree-collapsed');
        $(this).siblings('ul').slideToggle('fast');
      });

      // An expand all button (unimplemented)
      /*
      $('.expandbutton').click(function() {
        $(this).siblings('.term-reference-tree-button').trigger('click');
      });
      */


      $('.term-reference-tree', context).each(function() {
        // On page load, check whether the maximum number of choices is already selected.
        // If so, disable the other options.
        var tree = $(this);
        checkMaxChoices(tree, false);
        $(this).find('input[type=checkbox]').change(function() {
          checkMaxChoices(tree, $(this));
        });
        
        //On page load, check if the user wants a track list. If so, add the 
        //currently selected items to it.
        if($(this).hasClass('term-reference-tree-track-list-shown')) {
          var track_list_container = $(this).find('.term-reference-tree-track-list');
          
          //Var to track whether using checkboxes or radio buttons.
          var input_type =
            ( $(this).has('input[type=checkbox]').size() > 0 ) ? 'checkbox' : 'radio';
            
          //Find all the checked controls.
          var checked_controls = $(this).find('input[type=' + input_type + ']:checked');

          //Get their labels.
          var labels = checked_controls.next();
          var label_element;
          
          //For each label of the checked boxes, add item to the track list.
          labels.each(function(index) {
            label_element = $(labels[index]);
            addItemToTrackList(
              track_list_container,         //Where to add new item.
              label_element.html(),         //Text of new item.
              $(label_element).attr('for'), //Id of control new item is for.
              input_type                    //checkbox or radio
            );
          }); //End labels.each
          
          //Show "nothing selected" message, if needed.
          showNothingSelectedMessage(track_list_container);
          
          //Event - when an element on the track list is clicked on:
          //  1. Delete it.
          //  2. Uncheck the associated checkbox.
          //The event is bound to the track list container, not each element.
          $(track_list_container).click(function(event){
            //Remove the "nothing selected" message if showing - add it later if needed.
            //removeNothingSelectedMessage(track_list_container);
            var event_target = $(event.target);
            var control_id = event_target.data('control_id');

            if(control_id) {
              event_target.remove();

              var checkbox = $('#' + control_id);
              checkbox.removeAttr('checked');
              checkMaxChoices(tree, checkbox);

              //Show "nothing selected" message, if needed.
              showNothingSelectedMessage(track_list_container);
            }
          });
          
          //Change track list when controls are clicked.
          $(this).find('.form-' + input_type).change(function(event){
            //Remove the "nothing selected" message if showing - add it later if needed.
            removeNothingSelectedMessage(track_list_container);
            var event_target = $(event.target);
            var control_id = event_target.attr('id');
            if ( event_target.attr('checked') ) {
              //Control checked - add item to the track list.
              label_element = event_target.next();
              addItemToTrackList(
                track_list_container,         //Where to add new item.
                label_element.html(),         //Text of new item.
                $(label_element).attr('for'), //Id of control new item is for.
                input_type                    //checkbox or radio
              );
            }
            else {
              //Checkbox unchecked. Remove from the track list.
              $('#' + control_id + '_list').remove();
            }
            
            //Show "nothing selected" message, if needed.
            showNothingSelectedMessage(track_list_container);
          }); //End process checkbox changes.
        } //End Want a track list.
        
        //On page load, check if the user wants a cascading selection.
        if($(this).hasClass('term-reference-tree-cascading-selection')) {

          //Check children when checkboxes are clicked.
          $(this).find('.form-checkbox').change(function(event) {
            var event_target = $(event.target);
            var control_id = event_target.attr('id');
            var children = event_target.parent().next().children().children('div.form-type-checkbox').children('input[id^="' + control_id + '-children"]');
            if(event_target.attr('checked')) {
              //Checkbox checked - check children if none were checked.
              if(!$(children).filter(':checked').length) {
                $(children).click().trigger('change');
              }
            }
            else {
              //Checkbox unchecked. Uncheck children if all were checked.
              if(!$(children).not(':checked').length) {
                $(children).click().trigger('change');
              }
            }

          });
          //End process checkbox changes.
        } //End Want a cascading checking.

      });
    }
  };
  
  /**
   * Add a new item to the track list. 
   * If more than one item can be selected, the new item is positioned to 
   * match the order of the terms in the checkbox tree.
   * 
   * @param track_list_container Container where the new item will be added.
   * 
   * @param item_text Text of the item to add.
   * 
   * @param control_id Id of the checkbox/radio control the item matches.
   * 
   * @param control_type Control type - 'checkbox' or 'radio'.
   */
  function addItemToTrackList(track_list_container, item_text, control_id, control_type) {
    var new_item = $('<li class="track-item">' + item_text + '</li>');
    new_item.data('control_id', control_id);
    
    //Add an id for easy finding of the item.
    new_item.attr('id', control_id + '_list');
    
    //Process radio controls - only one item can be selected.
    if ( control_type == 'radio') {
      //Find the existing element on the track list, if there is one.
      var current_items = track_list_container.find('li');
      
      //If there are no items on the track list, add the new item.
      if ( current_items.size() == 0 ) {
        track_list_container.append(new_item);
      }
      else {
        //There is an item on the list.
        var current_item = $(current_items.get(0));
        
        //Is the item we want to add different from what is there?
        if ( current_item.data('control_id') != control_id ) {
          //Remove exiting element from track list, and add the new one.
          current_item.remove();
          track_list_container.append(new_item);
        }
      }
      return;
    }
    
    //Using checkboxes, so there can be more than one selected item.
    //Find the right place to put the new item, to match the order of the
    //checkboxes.
    var list_items = track_list_container.find('li');
    var item_comparing_to;
    
    //Flag to tell whether the item was inserted.
    var inserted_flag = false;
    list_items.each(function(index){
      item_comparing_to = $(list_items[index]);
      
      //If item is already on the track list, do nothing.
      if ( control_id == item_comparing_to.data('control_id') ) {
        inserted_flag = true;
        return false; //Returning false stops the loop.
      }
      else if ( control_id < item_comparing_to.data('control_id') ) {
        //Add it here.
        item_comparing_to.before(new_item);
        inserted_flag = true;
        return false; //Returning false stops the loop.
      }
    });
    
    //If not inserted yet, add new item at the end of the track list.
    if ( ! inserted_flag ) {
      track_list_container.append(new_item);
    }
  }
  
  /**
   * Show the 'nothing selected' message if it applies.
   * 
   * @param track_list_container Where the message is to be shown.
   */
  function showNothingSelectedMessage(track_list_container) {
    //Is the message there already?
    var message_showing = 
        (track_list_container.find('.term_ref_tree_nothing_message').size() != 0);
        
    //Number of real items showing.
    var num_real_items_showing = 
        message_showing 
        ? track_list_container.find('li').size() - 1
        : track_list_container.find('li').size();
    if ( num_real_items_showing == 0 ) {
      //No items showing, so show the message.
      if ( ! message_showing ) {
        track_list_container.append(
            '<li class="term_ref_tree_nothing_message">' + termReferenceTreeNothingSelectedText + '</li>'
        );
      }
    }
    else { // !(num_real_items_showing == 0)
      //There are real items.
      if ( message_showing ) {
        track_list_container.find('.term_ref_tree_nothing_message').remove();
      }
    }
  }

  /**
   * Remove the 'nothing selected' message. Makes processing easier.
   * 
   * @param track_list_container Where the message is shown.
   */
  function removeNothingSelectedMessage(track_list_container) {
    track_list_container.find('.term_ref_tree_nothing_message').remove();
  }

  // This helper function checks if the maximum number of choices is already selected.
  // If so, it disables all the other options.  If not, it enables them.
  function checkMaxChoices(item, checkbox) {
    var maxChoices = -1;
    try {
      maxChoices = parseInt(Drupal.settings.term_reference_tree.trees[item.attr('id')]['max_choices']);
    }
    catch (e){}
    var count = item.find(':checked').length;
    
    if(maxChoices > 0 && count >= maxChoices) {
      item.find('input[type=checkbox]:not(:checked)').attr('disabled', 'disabled').parent().addClass('disabled');
    } else {
      item.find('input[type=checkbox]').removeAttr('disabled').parent().removeClass('disabled');
    }

    if(checkbox) {
      if(item.hasClass('select-parents')) {
        var track_list_container = item.find('.term-reference-tree-track-list');
        var input_type =
            ( item.has('input[type=checkbox]').size() > 0 ) ? 'checkbox' : 'radio';
            
        if(checkbox.attr('checked')) {
          checkbox.parents('ul.term-reference-tree-level li').children('div.form-item').children('input[type=checkbox]').each(function() {
            $(this).attr('checked', checkbox.attr('checked'));
            
            if(track_list_container) {
              label_element = $(this).next();
              addItemToTrackList(
                track_list_container,         //Where to add new item.
                label_element.html(),         //Text of new item.
                $(label_element).attr('for'), //Id of control new item is for.
                input_type                    //checkbox or radio
              );
            }
          });
        }
      }
    }
  }
})(jQuery);