/**
 *  @file
 *  File with utilities to handle media in html editing.
 */
(function ($) {

  Drupal.media = Drupal.media || {};
  /**
   * Utility to deal with media tokens / placeholders.
   */
  Drupal.media.filter = {
    /**
     * Replaces media tokens with the placeholders for html editing.
     * @param content
     */
    replaceTokenWithPlaceholder: function(content) {
      Drupal.media.filter.ensure_tagmap()
      var tagmap = Drupal.settings.tagmap,
        matches = content.match(/\[\[.*?\]\]/g),
        media_definition;

      if (matches) {
        for (var macro in tagmap) {
          // We cant use indexOf because of IE.
          var index = $.inArray(macro, matches);
          if (index !== -1) {
            var media_json = macro.replace('[[', '').replace(']]', '');

            // Make sure that the media JSON is valid.
            try {
              media_definition = JSON.parse(media_json);
            }
            catch (err) {
              media_definition = null;
            }
            if (media_definition) {
              // Apply attributes.
              var element = Drupal.media.filter.create_element(tagmap[macro], media_definition);
              var markup = Drupal.media.filter.outerHTML(element);

              content = content.replace(macro, markup);
            }
          }
        }
      }
      return content;
    },

    /**
     * Replaces the placeholders for html editing with the media tokens to store.
     * @param content
     */
    replacePlaceholderWithToken: function(content) {
      Drupal.media.filter.ensure_tagmap();
      // Convert all xhtml markup to html for reliable matching/replacing.
      content = content.replace(/[\s]\/\>/g, '>');

      // Re-build the macros in case any element has changed in the editor.
      $('.media-element', content).each(function(i, element) {
        var markup = Drupal.media.filter.outerHTML($(element));
          macro = Drupal.media.filter.create_macro($(element));

        // Store the macro => html for more efficient rendering in
        // replaceTokenWithPlaceholder().
        Drupal.settings.tagmap[macro] = markup;
        // Replace the media element with its macro.
        content = content.replace(markup, macro);
      });

      return content;
    },

    /**
     * Serializes file information as a url-encoded JSON object and stores it as a
     * data attribute on the html element.
     *
     * @param html (string)
     *    A html element to be used to represent the inserted media element.
     * @param info (object)
     *    A object containing the media file information (fid, view_mode, etc).
     */
    create_element: function (html, info) {
      if ($('<div></div>').append(html).text().length === html.length) {
        // Element is not an html tag. Surround it in a span element
        // so we can pass the file attributes.
        html = '<span>' + html + '</span>';
      }
      var element = $(html);

      // Move attributes from the file info array to the placeholder element.
      if (info.attributes) {
        $.each(Drupal.settings.media.wysiwyg_allowed_attributes, function(i, a) {
          if (info.attributes[a]) {
            element.attr(a, info.attributes[a]);
          }
        });
        delete(info.attributes);
      }

      // Important to url-encode the file information as it is being stored in an
      // html data attribute.
      info.type = info.type || "media";
      element.attr('data-file_info', encodeURI(JSON.stringify(info)));

      // Adding media-element class so we can find markup element later.
      var classes = ['media-element'];

      if(info.view_mode){
        classes.push('file-' + info.view_mode.replace(/_/g, '-'));
      }
      element.addClass(classes.join(' '));

      return element;
    },

    /**
     * Create a macro representation of the inserted media element.
     *
     * @param element (jQuery object)
     *    A media element with attached serialized file info.
     */
    create_macro: function (element) {
      var file_info = Drupal.media.filter.extract_file_info(element);
      if (file_info) {
        return '[[' + JSON.stringify(file_info) + ']]';
      }
      return false;
    },

    /**
     * Extract the file info from a WYSIWYG placeholder element as JSON.
     *
     * @param element (jQuery object)
     *    A media element with attached serialized file info.
     */
    extract_file_info: function (element) {
      var file_json = $.data(element, 'file_info') || element.data('file_info'),
        file_info,
        value;

      try {
        file_info = JSON.parse(decodeURIComponent(file_json));
      }
      catch (err) {
        file_info = null;
      }

      if (file_info) {
        file_info.attributes = {};

        // Extract whitelisted attributes.
        $.each(Drupal.settings.media.wysiwyg_allowed_attributes, function(i, a) {
          if (value = element.attr(a)) {
            file_info.attributes[a] = value;
          }
        });
        delete(file_info.attributes['data-file_info']);

        // Extract the link text, if there is any.
        file_info.link_text = element.find('a').html();

      }

      return file_info;
    },

    /**
     * Gets the HTML content of an element.
     *
     * @param element (jQuery object)
     */
    outerHTML: function (element) {
      return element[0].outerHTML || $('<div>').append(element.eq(0).clone()).html();
    },

    /**
     * Gets the wrapped HTML content of an element to insert into the wysiwyg.
     *
     * It also registers the element in the tag map so that the token
     * replacement works.
     *
     * @param element (jQuery object) The element to insert.
     *
     * @see Drupal.media.filter.replacePlaceholderWithToken()
     */
    getWysiwygHTML: function (element) {
      // Create the markup and the macro.
      var markup = Drupal.media.filter.outerHTML(element),
        macro = Drupal.media.filter.create_macro(element);

      // Store macro/markup in the tagmap.
      Drupal.media.filter.ensure_tagmap();
      var i = 1;
      for (var key in Drupal.settings.tagmap) {
        i++;
      }
      Drupal.settings.tagmap[macro] = markup;

      // Return the html code to insert in an editor and use it with
      // replacePlaceholderWithToken()
      return markup;
    },

    /**
     * Ensures the tag map has been initialized and returns it.
     */
    ensure_tagmap: function () {
      Drupal.settings.tagmap = Drupal.settings.tagmap || {};
      return Drupal.settings.tagmap;
    }
  }
})(jQuery);
