class Prawn::SVG::Properties
  Config = Struct.new(:default, :inheritable?, :keywords, :keyword_restricted?, :attr, :ivar)

  EM = 16
  FONT_SIZES = {
    'xx-small' => EM / 4,
    'x-small'  => EM / 4 * 2,
    'small'    => EM / 4 * 3,
    'medium'   => EM / 4 * 4,
    'large'    => EM / 4 * 5,
    'x-large'  => EM / 4 * 6,
    'xx-large' => EM / 4 * 7
  }

  PROPERTIES = {
    "clip-path"        => Config.new("none", false, %w(inherit none)),
    "color"            => Config.new('', true),
    "display"          => Config.new("inline", false, %w(inherit inline none), true),
    "fill"             => Config.new("black", true, %w(inherit none currentColor)),
    "fill-opacity"     => Config.new("1", true),
    "fill-rule"        => Config.new("nonzero", true, %w(inherit nonzero evenodd)),
    "font-family"      => Config.new("sans-serif", true),
    "font-size"        => Config.new("medium", true, %w(inherit xx-small x-small small medium large x-large xx-large larger smaller)),
    "font-style"       => Config.new("normal", true, %w(inherit normal italic oblique), true),
    "font-variant"     => Config.new("normal", true, %w(inherit normal small-caps), true),
    "font-weight"      => Config.new("normal", true, %w(inherit normal bold 100 200 300 400 500 600 700 800 900), true), # bolder/lighter not supported
    "letter-spacing"   => Config.new("normal", true, %w(inherit normal)),
    "marker-end"       => Config.new("none", true, %w(inherit none)),
    "marker-mid"       => Config.new("none", true, %w(inherit none)),
    "marker-start"     => Config.new("none", true, %w(inherit none)),
    "opacity"          => Config.new("1", false),
    "overflow"         => Config.new('visible', false, %w(inherit visible hidden scroll auto), true),
    "stop-color"       => Config.new("black", false, %w(inherit none currentColor)),
    "stroke"           => Config.new("none", true, %w(inherit none currentColor)),
    "stroke-dasharray" => Config.new("none", true, %w(inherit none)),
    "stroke-linecap"   => Config.new("butt", true, %w(inherit butt round square), true),
    "stroke-opacity"   => Config.new("1", true),
    "stroke-width"     => Config.new("1", true),
    "text-anchor"      => Config.new("start", true, %w(inherit start middle end), true),
  }.freeze

  PROPERTIES.each do |name, value|
    value.attr = name.gsub("-", "_")
    value.ivar = "@#{value.attr}"
  end

  PROPERTY_CONFIGS = PROPERTIES.values
  NAMES = PROPERTIES.keys
  ATTR_NAMES = PROPERTIES.keys.map { |name| name.gsub('-', '_') }

  attr_accessor *ATTR_NAMES

  def load_default_stylesheet
    PROPERTY_CONFIGS.each do |config|
      instance_variable_set(config.ivar, config.default)
    end

    self
  end

  def set(name, value)
    if config = PROPERTIES[name.to_s.downcase]
      value = value.strip
      keyword = value.downcase
      keywords = config.keywords || ['inherit']

      if keywords.include?(keyword)
        value = keyword
      elsif config.keyword_restricted?
        value = config.default
      end

      instance_variable_set(config.ivar, value)
    end
  end

  def to_h
    PROPERTIES.each.with_object({}) do |(name, config), result|
      result[name] = instance_variable_get(config.ivar)
    end
  end

  def load_hash(hash)
    hash.each { |name, value| set(name, value) if value }
  end

  def compute_properties(other)
    PROPERTY_CONFIGS.each do |config|
      value = other.send(config.attr)

      if value && value != 'inherit'
        value = compute_font_size_property(value).to_s if config.attr == "font_size"
        instance_variable_set(config.ivar, value)

      elsif value.nil? && !config.inheritable?
        instance_variable_set(config.ivar, config.default)
      end
    end
  end

  def numerical_font_size
    # px = pt for PDFs
    FONT_SIZES[font_size] || font_size.to_f
  end

  private

  def compute_font_size_property(value)
    if value[-1] == "%"
      numerical_font_size * (value.to_f / 100.0)
    elsif value == 'larger'
      numerical_font_size + 4
    elsif value == 'smaller'
      numerical_font_size - 4
    elsif value.match(/(\d|\.)em\z/i)
      numerical_font_size * value.to_f
    elsif value.match(/(\d|\.)rem\z/i)
      value.to_f * EM
    else
      FONT_SIZES[value] || value.to_f
    end
  end
end
