#
# This class help the creation of material icons in the UI.
#
class MaterialIcon
  # To use content_tag
  include ActionView::Helpers::TagHelper

  # Undefined method will ref to the icon.
  def method_missing(name)
    @icon =
      if name == :class_icon
        'class' # Set the icon named 'class'
      else
        clear_icon(name)
      end
    self
  end

  #
  # Reset will set all variables to nil
  #
  def reset
    @icon, @rotation, @size, @html, @style, @css_class = [nil] * 6
    self
  end

  #
  # Define rotate methods
  #
  %w(r90 r180 r270 flip_horizontal flip_vertical).each do |rotation|
    define_method(rotation) do
      @rotation = " #{rotation.gsub('_', '-')}"
      self
    end
  end

  #
  # Define size methods
  #
  %w(md_18 md_24 md_36 md_48).each do |size|
    define_method(size) do
      @size = " #{size.gsub('_', '-')} "
      self
    end
  end

  #
  # Add a CSS class to :i tag
  #
  # == Paremeters:
  # css_class::
  #    String with CSS classes
  #
  # == Returns:
  # MaterialIcon instance
  #
  def css_class(css_class = '')
    @css_class = " #{css_class}"
    self
  end

  #
  # Add CSS properties to :i tag
  #
  # == Paremeters:
  # style::
  #    String with CSS rules
  #
  # == Returns:
  # MaterialIcon instance
  #
  def style(css_style = '')
    if css_style && css_style.empty?
      # It references style icon
      @icon = clear_icon('style')
    else
      # User wants to apply a style to the icon
      @style = css_style
    end
    self
  end

  # Create an alias to use the original method
  alias_method :super_send, :send

  #
  # Override send functionality to set the name of the icon to "send" when
  # the method doesn't receive any parameters
  #
  # == Paremeters:
  # name::
  #    Name of the method to call
  # args::
  #    Arguments to send the method
  #
  # == Returns:
  # MaterialIcon instance or the result of the method call
  #
  def send(name = '', *args)
    if name && name.empty?
      @icon = clear_icon('send')
      # Return self
      self
    else
      super_send(name.to_sym, *args)
    end
  end

  #
  # Add HTML options to :i tag.
  #
  # == Paremeters:
  # html::
  #   Hash with options to add to :i tag. For example:
  #     { data: { id: 1 } }
  #
  # == Returns:
  # MaterialIcon instance
  #
  def html(html = {})
    @html = html
    self
  end

  #
  # Check based on rails config if the selected mode is unicode
  #
  def unicode?
    MaterialIcons.unicode
  end

  #
  # Create the HTML code for the icon. This method check if selected mode is
  # unicode or ligatures.
  #
  # == Returns:
  # Safe string
  #
  def to_s
    # Sanitize html
    @html = @html.nil? || !@html.is_a?(Hash) ? {} : @html

    # Create the icon
    if unicode?
      content_tag(:i, '',
                  @html.merge(
                    style: @style,
                    class: "material-icons #{@icon}#{@size}#{@rotation}#{@css_class}"))
    else
      content_tag(:i, "#{@icon}",
                  @html.merge(
                    style: @style,
                    class: "material-icons#{@size}#{@rotation}#{@css_class}"))
    end
  end

  # Fix #13. Some Rails methods uses to_str instead of to_s. In this case, to_str is handled
  # by method_missing so it's returning the same instance and the method are failing
  alias_method :to_str, :to_s

  #
  # Some methods require to_ary to be defined. We return nil in this method to avoid this class
  # to be threated as an Array
  #
  # == Returns
  # Nil
  #
  def to_ary
    nil
  end

  private

  #
  # Convert icon names that start with a number to the correct format. On
  # Material Design Icons version 2.0.0, unique conflict is the 3d-rotation
  # icon
  #
  # == Parameters:
  # icon:
  #   String with the name of the icon
  #
  # == Returns:
  # An string with filtered name of the icon
  #
  def clear_icon(icon)
    return icon if unicode? || icon != :three_d_rotation
    '3d_rotation'
  end
end
