#
# This file is part of the ballast gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
#

module Ballast
  # A module to ease emoji handling.
  module Emoji
    # General utility methods.
    #
    # @attribute url_mapper
    #   @return [Proc] The current URL mapper or a default one (which will return the relative URL unmodified).
    module Utils
      attr_accessor :url_mapper

      # Returns the regular expression which matches all the known emojis.
      #
      # @return [Regexp] The regular expression which matches all the known emojis.
      def replace_regex
        @replace_regex ||= /(#{::Emoji.send(:unicodes_index).keys.join("|")})/
      end

      # Replaces all the emojis in the text using the requested mod.
      #
      # @param text [String] The text to manipulate.
      # @param mode [Symbol] The method to use when replacing icons.
      # @param options [Hash] The options to pass to the replacing method.
      # @return [String] The text with all emojis replaced.
      def replace(text, mode: :html, **options)
        mode = :markup unless mode && ::Emoji::Character.new(nil).respond_to?(mode)
        text.ensure_string.gsub(replace_regex) { invoke(::Emoji.find_by_unicode(Regexp.last_match[1]), mode, options) }
      end

      # Lists all the emoji known in a hash.
      #
      # @param keys_method [Symbol] The method to use for keys.
      # @param values_method [Symbol] The method to use for values.
      # @param options [Hash] The options to pass to all methods.
      # @return [Hash] A hash of all known emojis.
      def enumerate(keys_method: :markup, values_method: :html, **options)
        tester = ::Emoji::Character.new(nil)
        keys_method = :markup unless keys_method && tester.respond_to?(keys_method)
        values_method = :html unless values_method && tester.respond_to?(values_method)

        ::Emoji.all.reduce({}) { |accu, icon|
          accu[invoke(icon, keys_method, options)] = invoke(icon, values_method, options)
          accu
        }
      end

      # Returns the URL mapper for the emojis.
      #
      # @return [Proc] The current URL mapper or a default one (which will return the relative URL unmodified).
      def url_mapper
        @url_mapper || ->(url) { url }
      end

      # Returns a absolute URL for a emoji image.
      #
      # @param image [String] The relative URL of the emoji filename.
      # @return [String] The absolute URL of the emoji filename.
      def url_for(image)
        url_mapper.call(image)
      end

      private

      # :nodoc:
      def invoke(subject, method, options)
        subject.method(method).arity == 1 ? subject.send(method, options) : subject.send(method)
      end
    end

    # Extensions for a emoji character.
    module Character
      include ActionView::Helpers::TagHelper
      include ActiveSupport::Concern

      # Returns a markup for the current character.
      #
      # @return [String] The markup for a character.
      def markup
        ":#{name}:"
      end

      # Returns a image URL for the current character.
      #
      # @return [String] The image URL for the current character.
      def url
        ::Emoji.url_for(image_filename)
      end

      # Returns a image tag for the current character.
      # @see ActionView::Helpers::TagHelper#tag
      #
      # @return [String] The options for the tag generation.
      def image_tag(options = {})
        options = options.reverse_merge({alt: markup, title: markup, rel: "tooltip"})
        classes = options[:class].ensure_string.tokenize(pattern: /[\s,]+/, no_duplicates: true)
        classes << "emoji" unless classes.include?("emoji")

        options[:src] = url
        options[:class] = classes.uniq.join(" ")

        tag(:img, options)
      end
      alias_method :image, :url
      alias_method :html, :image_tag
    end
  end
end

::Emoji.extend(Ballast::Emoji::Utils)
::Emoji::Character.include(Ballast::Emoji::Character)