// @requires: jQuery Cascade (jquery.cascade.js)
// @requires: jQuery Tools Overlay (http://flowplayer.org/tools/)
(function($) {
  $.getJSON("/cancer.json", function(data) {
    var cancers = $.map(data, function(obj, i) {
      return obj[plugin.arJsonRootKey] || obj;
    });
    Array.prototype.splice.apply(plugin.conf.cascade.list, [0, 0].concat(cancers));
    $(document).trigger("downloaded.cancer_types", [plugin.conf.cascade.list]);
  });
  $.fn.cancerCategorization = function(opts) {
    return this.each(function() {
      function select(val){
        return selection = parseInt(val, 10);
      }
      var options = $.extend(true, {}, plugin.conf, opts),
          container  = $(this),
          selects    = $("select", this),
          valueInput = $("input[type=hidden]", this),
          modal      = $("#"+ options.modalId),
          overlay    = modal.overlay(options.overlay),
          modalOption = plugin.conf.cascade.template.call(plugin.conf.cascade.moreOptionData),
          selection;

      // if data has not yet been retrieved, initializes $.fn.cascade with an empty array
      // once data is present, passed array will be mutated (.splice()'d) with new data
      setupCascade(selects, options);

      selects.eq(0)
        .bind("change", overlay, openModal)
        // display modal when a user clicks the more types option, even if its already selected
        .bind("click",  overlay, openModal)
        .append(modalOption);

      modal.find(".submit").click(function(e) {
        select(modal.find("input:checked").val() || valueInput.val());
        restoreSelections(selects, selection);
        overlay.close();
      });
      overlay.getClosers().filter("a[href=#submit-modal]").click(function(e) {
        select(modal.find("input:checked").val() || valueInput.val());
        restoreSelections(selects, selection);
      });
      overlay.getClosers().not("a[href=#submit-modal]").click(function(e) {
        select(valueInput.val());
        restoreSelections(selects, selection);
      });
      // TODO: figure out why this prevents overlay dismissal in IE.
      // correct this to prevent #submit-modal anchor appended on modal close.
      // overlay.onClose(function(e){
      //   if (e.originalEvent) { e.originalEvent.preventDefault(); }
      //   return e.preventDefault();
      // });

      selects.each(hideEmpty);
      // only update stored value when last select is updated by the cascade
      // changes to any select will result in change.cascade getting called on the last select
      selects.eq(selects.length - 1).bind("change.cascade", [selects, valueInput], storeValue);
      selects.bind("loaded.cascade", hideEmpty);

      select($("option:selected:last", this).val());
    });
  };
  var plugin = $.cancerCategorization = {
    arJsonRootKey: "cancer_type",
    conf: {
      modalId: "cancer_type_modal",
      overlay: {
        expose: {
          color: "#333",
          loadSpeed: 200,
          zIndex: 9990
        },
        api: true
      },
      cascade: {
        list: [],
        moreOptionData: {
          id: "",
          display_name: "More cancer types »"
        },
        match: function cascadeMatch(parentValue) {
          return ((this.id === parentValue) || (this.parent_id === parentValue));
        },
        template: function cascadeTemplate(parentVal) {
          var text = (this.id == parseInt(parentVal, 10)) ? this.parent_name : this.display_name;
          return $("<option />").attr("value", this.id).text(text);
        },
        // special casing to deal with parent/child dual existence
        getParentValue: function cascadeGetParentValue(parent){
          var parentValue = $(parent).val();
          var parentData = $(parent).data("cascade") || {};
          if (parentData.source && (parentValue == parentData.source.val())) {
            return false;
          } else {
            return parseInt(parentValue, 10);
          }
        }
      }
    }
  };

  function setupCascade(selects, options){
    var parent, child;
    selects.each(function(index) {
      child = $(this);
      if (parent) {
        child.cascade(parent, {
          list: plugin.conf.cascade.list,
          match: plugin.conf.cascade.match,
          template: plugin.conf.cascade.template,
          // special casing to deal with parent/child dual existence
          getParentValue: function cascadeGetParentValue(parent){
            var parentValue = $(parent).val();
            var parentData = $(parent).data("cascade") || {};
            if (parentData.source && (parentValue == parentData.source.val())) {
              return false;
            } else {
              return parseInt(parentValue, 10);
            }
          }
        }).data("cascade", { source: parent });
      }
      parent = child;
    });
  }
  
  // restore user selection if not in the first select box
  function restoreSelections(selects, selection){
    var selectedItem = findFirst(selection, plugin.conf.cascade.list);
    if (selectedItem) {
      var selections     = getAncestorsTo(selectedItem),
          rootSelect     = selects.eq(0),
          firstSelection = selections[0];

      $.each(selections, function(i, selection) {
        // skip the first select while attaching handlers: the root select
        // will be triggering these "loaded.cascade" handlers momentarily
        if (i == 0) return;
        var select = selects.eq(i);
        // wait for the cascade, then set select to the selected value
        select.one("loaded.cascade", function() {
          select.val(selection.id);
        });
      });

      // test if the root select already contains the selected value
      if (rootSelect.val(firstSelection.id).val() != firstSelection.id) {
        // build an option from the selection
        var newOption = plugin.conf.cascade.template.call(firstSelection);
        // add the new option to the root select box, before the "More Cancer Types" option
        rootSelect.find("option:last").before(newOption);
        // only set the value of the root select box, the others will get their
        // options from cascade and values from the "loaded.cascade" handlers attached above
        rootSelect.val(firstSelection.id);
      }
      // this triggers the cascade through all of the selects.
      // do it after our event handlers have been registered, above
      rootSelect.trigger("change.cascade");
    }
  }
  function findFirst(sought, list){
    if (!sought) return;
    return $.grep(list, function(obj, i) {
      return obj.id === sought;
    })[0];
  }
  function getAncestorsTo(item){
    var ancestors = getAncestorsOf(item);
    return [item].concat(ancestors).reverse();
  }
  function getAncestorsOf(item){
    if (item.parent_id) {
      var parent = findFirst(item.parent_id, plugin.conf.cascade.list);
      return [parent].concat(getAncestorsOf(parent));
    } else {
      return [];
    }
  }
  function openModal(e){
    // can't find by value because the prompt text has "" value also
    // if ($("option:selected", this).text() == lastOptionText) {
    if ($("option:selected", this).is(":last-child")) {
      e.data.load();
    }
  }
  function storeValue(e){
    var selects = e.data[0], hidden = e.data[1], newValue;
    selects.each(function(){
      newValue = $(this).val() || newValue;
    });

    App.debug("storeValue", newValue);
    hidden.val(newValue || '');
  }
  function hideEmpty(e){
    var target = $(this);
    target.toggle(!target.is(":empty"));
  }
})(jQuery);