/**
 * @file
 * dialog.js
 */
(function ($, Drupal, Bootstrap, Attributes) {

  Bootstrap.Dialog = Bootstrap.Dialog || {};

  /**
   * A collection of Drupal dialog handlers.
   *
   * @type {Object<String, Drupal.bootstrap.Dialog.Handler>}
   */
  Bootstrap.Dialog.handlers = {};

  /**
   * @class Drupal.bootstrap.Dialog.Handler
   *
   * @param type
   * @param data
   */
  Bootstrap.Dialog.Handler = function (type, data) {
    this.ctor = $.fn.modal;
    this.extend = null;
    this.plugin = 'modal';
    this.prefix = 'modal';
    this.themeHooks = {
      modal: 'bootstrapModal',
      dialog: 'bootstrapModalDialog',
      header: 'bootstrapModalHeader',
      title: 'bootstrapModalTitle',
      close: 'bootstrapModalClose',
      content: 'bootstrapModalContent',
      body: 'bootstrapModalBody',
      footer: 'bootstrapModalFooter',
    };
    this.type = type;
    this.selectors = {
      dialog: '.modal-dialog',
      header: '.modal-header',
      title: '.modal-title',
      close: '.close',
      content: '.modal-content',
      body: '.modal-body',
      footer: '.modal-footer',
      buttons: '.modal-buttons'
    };

    // Extend the object with subclassed data.
    $.extend(this, data);

    // Extend the jQuery plugin.
    if (this.extend) {
      Bootstrap.extend(this.plugin, this.extend);
    }
  };

  /**
   * Retrieves a Drupal dialog type handler.
   *
   * @param {String|HTMLElement|jQuery} type
   *   The dialog type to retrieve.
   *
   * @return {Drupal.bootstrap.Dialog.Handler}
   *   A Bootstrap.Dialog.Handler instance.
   */
  Bootstrap.Dialog.Handler.get = function (type) {
    if (type instanceof $) {
      type = type[0];
    }
    if (type instanceof HTMLElement) {
      type = type.dialogType;
    }
    if (!type) {
      type = 'modal';
    }
    if (!Bootstrap.Dialog.handlers[type]) {
      Bootstrap.Dialog.handlers[type] = new Bootstrap.Dialog.Handler();
    }
    return Bootstrap.Dialog.handlers[type];
  };

  /**
   * Registers a Drupal dialog type handler.
   *
   * @param {String} type
   *   The dialog type to
   * @param {Object} [data]
   *   Optional. Additional data to use to create the dialog handler. By
   *   default, this assumes values relative to the Bootstrap Modal plugin.
   */
  Bootstrap.Dialog.Handler.register = function (type, data) {
    Bootstrap.Dialog.handlers[type] = new Bootstrap.Dialog.Handler(type, data);
  };

  Bootstrap.Dialog.Handler.prototype.invoke = function (context) {
    var args = Array.prototype.slice.call(arguments);
    return this.ctor.apply(context, args.slice(1));
  };

  Bootstrap.Dialog.Handler.prototype.theme = function (hook) {
    var args = Array.prototype.slice.call(arguments);
    return $(Drupal.theme.apply(Drupal.theme, [this.themeHooks[hook]].concat(args.slice(1))));
  };

  /**
   * Ensures a DOM element has the appropriate structure for a modal.
   *
   * Note: this can get a little tricky. Core potentially already
   * semi-processes a "dialog" if was created using an Ajax command
   * (i.e. prepareDialogButtons in drupal.ajax.js). Because of this, the
   * contents (HTML) of the existing element cannot simply be dumped into a
   * newly created modal. This would destroy any existing event bindings.
   * Instead, the contents must be "moved" (appended) to the new modal and
   * then "moved" again back to the to the existing container as needed.
   *
   * @param {HTMLElement|jQuery} element
   *   The element to ensure is a modal structure.
   * @param {Object} options
   *   THe dialog options to use to construct the modal.
   */
  Bootstrap.Dialog.Handler.prototype.ensureModalStructure = function (element, options) {
    var $element = $(element);

    // Immediately return if the modal was already converted into a proper modal.
    if ($element.is('[data-drupal-theme="' + this.themeHooks.modal + '"]')) {
      return;
    }

    var attributes = Attributes.create(element).remove('style').set('data-drupal-theme', this.themeHooks.modal);

    // Merge in trigger data attributes.
    if (options.$trigger && options.$trigger[0]) {
      /** @var {HTMLElement} trigger */
      var trigger = options.$trigger[0];
      var data = {};
      for (var i = 0, l = trigger.attributes.length; i < l; i++) {
        var name = trigger.attributes[i].name;
        if (name && name.substring(0, 5) === 'data-') {
          data[name] = trigger.getAttribute(name);
        }
      }
      attributes.merge(data);
    }

    options = $.extend(true, {}, options, {
      attributes: attributes,
    });

    // Create a new modal.
    var $modal = this.theme('modal', options);

    // Store a reference to the content inside the existing element container.
    // This references the actual DOM node elements which will allow
    // jQuery to "move" then when appending below. Using $.fn.children()
    // does not return any text nodes present and $.fn.html() only returns
    // a string representation of the content, which effectively destroys
    // any prior event bindings or processing.
    var $body = $element.find(this.selectors.body);
    var $existing = $body[0] ? $body.contents() : $element.contents();

    // Set the attributes of the dialog to that of the newly created modal.
    $element.attr(Attributes.create($modal).toPlainObject());

    // Append the newly created modal markup.
    $element.append($modal.html());

    // Move the existing HTML into the modal markup that was just appended.
    $element.find(this.selectors.body).append($existing);
  };

})(jQuery, Drupal, Drupal.bootstrap, Attributes);
