class Rouge::Formatters::Terminal256::EscapeSequence

def self.closest_color(r, g, b)

def self.closest_color(r, g, b)
  @@colors_cache ||= {}
  key = (r << 16) + (g << 8) + b
  @@colors_cache.fetch(key) do
    distance = MAX_DISTANCE
    match = 0
    xterm_colors.each_with_index do |(cr, cg, cb), i|
      d = (r - cr)**2 + (g - cg)**2 + (b - cb)**2
      next if d >= distance
      match = i
      distance = d
    end
    match
  end
end

def self.color_index(color)

def self.color_index(color)
  @color_index_cache ||= {}
  @color_index_cache[color] ||= closest_color(*get_rgb(color))
end

def self.get_rgb(color)

def self.get_rgb(color)
  color = $1 if color =~ /#([0-9a-f]+)/i
  hexes = case color.size
  when 3
    color.chars.map { |c| "#{c}#{c}" }
  when 6
    color.scan(/../)
  else
    raise "invalid color: #{color}"
  end
  hexes.map { |h| h.to_i(16) }
end

def self.xterm_colors

def self.xterm_colors
  @xterm_colors ||= [].tap do |out|
    # colors 0..15: 16 basic colors
    out << [0x00, 0x00, 0x00] # 0
    out << [0xcd, 0x00, 0x00] # 1
    out << [0x00, 0xcd, 0x00] # 2
    out << [0xcd, 0xcd, 0x00] # 3
    out << [0x00, 0x00, 0xee] # 4
    out << [0xcd, 0x00, 0xcd] # 5
    out << [0x00, 0xcd, 0xcd] # 6
    out << [0xe5, 0xe5, 0xe5] # 7
    out << [0x7f, 0x7f, 0x7f] # 8
    out << [0xff, 0x00, 0x00] # 9
    out << [0x00, 0xff, 0x00] # 10
    out << [0xff, 0xff, 0x00] # 11
    out << [0x5c, 0x5c, 0xff] # 12
    out << [0xff, 0x00, 0xff] # 13
    out << [0x00, 0xff, 0xff] # 14
    out << [0xff, 0xff, 0xff] # 15
    # colors 16..232: the 6x6x6 color cube
    valuerange = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
    217.times do |i|
      r = valuerange[(i / 36) % 6]
      g = valuerange[(i / 6) % 6]
      b = valuerange[i % 6]
      out << [r, g, b]
    end
    # colors 233..253: grayscale
    1.upto 22 do |i|
      v = 8 + i * 10
      out << [v, v, v]
    end
  end
end

def bg

def bg
  return @bg if instance_variable_defined? :@bg
  @bg = style.bg && self.class.color_index(style.bg)
end

def escape(attrs)

def escape(attrs)
  return '' if attrs.empty?
  "\e[#{attrs.join(';')}m"
end

def fg

def fg
  return @fg if instance_variable_defined? :@fg
  @fg = style.fg && self.class.color_index(style.fg)
end

def initialize(style)

def initialize(style)
  @style = style
end

def reset_string

def reset_string
  @reset_string ||= begin
    attrs = []
    attrs << '39' if fg # fg reset
    attrs << '49' if bg # bg reset
    attrs << '00' if style[:bold] || style[:italic]
    escape(attrs)
  end
end

def stream_value(val, &b)

def stream_value(val, &b)
  yield style_string
  yield val.gsub("\e", "\\e")
           .gsub("\n", "#{reset_string}\n#{style_string}")
  yield reset_string
end

def style_string

def style_string
  @style_string ||= begin
    attrs = []
    attrs << ['38', '5', fg.to_s] if fg
    attrs << ['48', '5', bg.to_s] if bg
    attrs << '01' if style[:bold]
    attrs << '04' if style[:italic] # underline, but hey, whatevs
    escape(attrs)
  end
end