require "spec"
require "colorize"
require "../support/env"

private def colorize(obj, *args)
  obj.colorize(*args).toggle(true)
end

private def with_color_wrap(*args)
  Colorize.with(*args).toggle(true)
end

private class ColorizeToS
  def to_s(io)
    io << "hello"
    io << ::colorize("world").blue
    io << "bye"
  end
end

private class ColorizeTTY < IO
  def tty? : Bool
    true
  end

  def read(slice : Bytes)
    0
  end

  def write(slice : Bytes) : Nil
  end
end

describe "colorize" do
  it ".default_enabled?" do
    io = IO::Memory.new
    tty = ColorizeTTY.new

    with_env("TERM": nil, "NO_COLOR": nil) do
      Colorize.default_enabled?(io).should be_false
      Colorize.default_enabled?(tty).should be_true
      Colorize.default_enabled?(io, io).should be_false
      Colorize.default_enabled?(io, tty).should be_false
      Colorize.default_enabled?(tty, io).should be_false
      Colorize.default_enabled?(tty, tty).should be_true
    end

    with_env("TERM": nil, "NO_COLOR": "") do
      Colorize.default_enabled?(io).should be_false
      Colorize.default_enabled?(tty).should be_true
      Colorize.default_enabled?(io, io).should be_false
      Colorize.default_enabled?(io, tty).should be_false
      Colorize.default_enabled?(tty, io).should be_false
      Colorize.default_enabled?(tty, tty).should be_true
    end

    with_env("TERM": nil, "NO_COLOR": "1") do
      Colorize.default_enabled?(io).should be_false
      Colorize.default_enabled?(tty).should be_false
      Colorize.default_enabled?(io, io).should be_false
      Colorize.default_enabled?(io, tty).should be_false
      Colorize.default_enabled?(tty, io).should be_false
      Colorize.default_enabled?(tty, tty).should be_false
    end

    with_env("TERM": "xterm", "NO_COLOR": nil) do
      Colorize.default_enabled?(io).should be_false
      Colorize.default_enabled?(tty).should be_true
      Colorize.default_enabled?(io, io).should be_false
      Colorize.default_enabled?(io, tty).should be_false
      Colorize.default_enabled?(tty, io).should be_false
      Colorize.default_enabled?(tty, tty).should be_true
    end

    with_env("TERM": "dumb", "NO_COLOR": nil) do
      Colorize.default_enabled?(io).should be_false
      Colorize.default_enabled?(tty).should be_false
      Colorize.default_enabled?(io, io).should be_false
      Colorize.default_enabled?(io, tty).should be_false
      Colorize.default_enabled?(tty, io).should be_false
      Colorize.default_enabled?(tty, tty).should be_false
    end
  end

  it "colorizes without change" do
    colorize("hello").to_s.should eq("hello")
  end

  it "colorizes foreground" do
    colorize("hello").black.to_s.should eq("\e[30mhello\e[39m")
    colorize("hello").red.to_s.should eq("\e[31mhello\e[39m")
    colorize("hello").green.to_s.should eq("\e[32mhello\e[39m")
    colorize("hello").yellow.to_s.should eq("\e[33mhello\e[39m")
    colorize("hello").blue.to_s.should eq("\e[34mhello\e[39m")
    colorize("hello").magenta.to_s.should eq("\e[35mhello\e[39m")
    colorize("hello").cyan.to_s.should eq("\e[36mhello\e[39m")
    colorize("hello").light_gray.to_s.should eq("\e[37mhello\e[39m")
    colorize("hello").dark_gray.to_s.should eq("\e[90mhello\e[39m")
    colorize("hello").light_red.to_s.should eq("\e[91mhello\e[39m")
    colorize("hello").light_green.to_s.should eq("\e[92mhello\e[39m")
    colorize("hello").light_yellow.to_s.should eq("\e[93mhello\e[39m")
    colorize("hello").light_blue.to_s.should eq("\e[94mhello\e[39m")
    colorize("hello").light_magenta.to_s.should eq("\e[95mhello\e[39m")
    colorize("hello").light_cyan.to_s.should eq("\e[96mhello\e[39m")
    colorize("hello").white.to_s.should eq("\e[97mhello\e[39m")
  end

  it "colorizes foreground with 8-bit color" do
    colorize("hello").fore(Colorize::Color256.new(123u8)).to_s.should eq("\e[38;5;123mhello\e[39m")
    colorize("hello").fore(123u8).to_s.should eq("\e[38;5;123mhello\e[39m")
    colorize("hello", 123_u8).to_s.should eq("\e[38;5;123mhello\e[39m")
  end

  it "colorizes foreground with true color" do
    colorize("hello").fore(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq("\e[38;2;12;34;56mhello\e[39m")
    colorize("hello").fore(12u8, 34u8, 56u8).to_s.should eq("\e[38;2;12;34;56mhello\e[39m")
    colorize("hello", 12u8, 34u8, 56u8).to_s.should eq("\e[38;2;12;34;56mhello\e[39m")
  end

  it "colorizes background" do
    colorize("hello").on_black.to_s.should eq("\e[40mhello\e[49m")
    colorize("hello").on_red.to_s.should eq("\e[41mhello\e[49m")
    colorize("hello").on_green.to_s.should eq("\e[42mhello\e[49m")
    colorize("hello").on_yellow.to_s.should eq("\e[43mhello\e[49m")
    colorize("hello").on_blue.to_s.should eq("\e[44mhello\e[49m")
    colorize("hello").on_magenta.to_s.should eq("\e[45mhello\e[49m")
    colorize("hello").on_cyan.to_s.should eq("\e[46mhello\e[49m")
    colorize("hello").on_light_gray.to_s.should eq("\e[47mhello\e[49m")
    colorize("hello").on_dark_gray.to_s.should eq("\e[100mhello\e[49m")
    colorize("hello").on_light_red.to_s.should eq("\e[101mhello\e[49m")
    colorize("hello").on_light_green.to_s.should eq("\e[102mhello\e[49m")
    colorize("hello").on_light_yellow.to_s.should eq("\e[103mhello\e[49m")
    colorize("hello").on_light_blue.to_s.should eq("\e[104mhello\e[49m")
    colorize("hello").on_light_magenta.to_s.should eq("\e[105mhello\e[49m")
    colorize("hello").on_light_cyan.to_s.should eq("\e[106mhello\e[49m")
    colorize("hello").on_white.to_s.should eq("\e[107mhello\e[49m")
  end

  it "colorizes background with 8-bit color" do
    colorize("hello").back(Colorize::Color256.new(123u8)).to_s.should eq("\e[48;5;123mhello\e[49m")
    colorize("hello").back(123u8).to_s.should eq("\e[48;5;123mhello\e[49m")
  end

  it "colorizes background with true color" do
    colorize("hello").back(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq("\e[48;2;12;34;56mhello\e[49m")
    colorize("hello").back(12u8, 34u8, 56u8).to_s.should eq("\e[48;2;12;34;56mhello\e[49m")
  end

  it "colorizes mode" do
    colorize("hello").bold.to_s.should eq("\e[1mhello\e[22m")
    colorize("hello").bright.to_s.should eq("\e[1mhello\e[22m")
    colorize("hello").dim.to_s.should eq("\e[2mhello\e[22m")
    colorize("hello").italic.to_s.should eq("\e[3mhello\e[23m")
    colorize("hello").underline.to_s.should eq("\e[4mhello\e[24m")
    colorize("hello").blink.to_s.should eq("\e[5mhello\e[25m")
    colorize("hello").blink_fast.to_s.should eq("\e[6mhello\e[25m")
    colorize("hello").reverse.to_s.should eq("\e[7mhello\e[27m")
    colorize("hello").hidden.to_s.should eq("\e[8mhello\e[28m")
    colorize("hello").strikethrough.to_s.should eq("\e[9mhello\e[29m")
    colorize("hello").double_underline.to_s.should eq("\e[21mhello\e[24m")
    colorize("hello").overline.to_s.should eq("\e[53mhello\e[55m")
  end

  it "prints colorize ANSI escape codes" do
    Colorize.with.bold.ansi_escape.should eq("\e[1m")
    Colorize.with.bright.ansi_escape.should eq("\e[1m")
    Colorize.with.dim.ansi_escape.should eq("\e[2m")
    Colorize.with.italic.ansi_escape.should eq("\e[3m")
    Colorize.with.underline.ansi_escape.should eq("\e[4m")
    Colorize.with.blink.ansi_escape.should eq("\e[5m")
    Colorize.with.blink_fast.ansi_escape.should eq("\e[6m")
    Colorize.with.reverse.ansi_escape.should eq("\e[7m")
    Colorize.with.hidden.ansi_escape.should eq("\e[8m")
    Colorize.with.strikethrough.ansi_escape.should eq("\e[9m")
    Colorize.with.double_underline.ansi_escape.should eq("\e[21m")
    Colorize.with.overline.ansi_escape.should eq("\e[53m")
  end

  it "only prints colorize ANSI escape codes" do
    colorize("hello").red.bold.ansi_escape.should eq("\e[31;1m")
    colorize("hello").bold.dim.underline.blink.reverse.hidden.ansi_escape.should eq("\e[1;2;4;5;7;8m")
  end

  it "colorizes mode combination" do
    colorize("hello").bold.dim.underline.blink.reverse.hidden.to_s.should eq("\e[1;2;4;5;7;8mhello\e[22;22;24;25;27;28m")
  end

  it "colorizes foreground with background" do
    colorize("hello").blue.on_green.to_s.should eq("\e[34;42mhello\e[39;49m")
  end

  it "colorizes foreground with background with mode" do
    colorize("hello").blue.on_green.bold.to_s.should eq("\e[34;42;1mhello\e[39;49;22m")
  end

  it "colorizes foreground with symbol" do
    colorize("hello", :red).to_s.should eq("\e[31mhello\e[39m")
    colorize("hello").fore(:red).to_s.should eq("\e[31mhello\e[39m")
  end

  it "colorizes mode with symbol" do
    colorize("hello").mode(:bold).to_s.should eq("\e[1mhello\e[22m")
  end

  it "raises on unknown foreground color" do
    expect_raises ArgumentError, "Unknown color: brown" do
      colorize("hello", :brown)
    end
  end

  it "raises on unknown background color" do
    expect_raises ArgumentError, "Unknown color: brown" do
      colorize("hello").back(:brown)
    end
  end

  it "inspects" do
    colorize("hello", :red).inspect.should eq("\e[31m\"hello\"\e[39m")
  end

  it "colorizes with surround" do
    io = IO::Memory.new
    with_color_wrap.red.surround(io) do
      io << "hello"
      with_color_wrap.green.surround(io) do
        io << "world"
      end
      io << "bye"
    end
    io.to_s.should eq("\e[31mhello\e[39;32mworld\e[39;31mbye\e[39m")
  end

  it "colorizes with surround and reset" do
    io = IO::Memory.new
    with_color_wrap.red.surround(io) do
      io << "hello"
      with_color_wrap.green.bold.surround(io) do
        io << "world"
      end
      io << "bye"
    end
    io.to_s.should eq("\e[31mhello\e[39;32;1mworld\e[39;22;31mbye\e[39m")
  end

  it "colorizes with surround and no reset" do
    io = IO::Memory.new
    with_color_wrap.red.surround(io) do
      io << "hello"
      with_color_wrap.red.surround(io) do
        io << "world"
      end
      io << "bye"
    end
    io.to_s.should eq("\e[31mhelloworldbye\e[39m")
  end

  it "colorizes with surround and default" do
    io = IO::Memory.new
    with_color_wrap.red.surround(io) do
      io << "hello"
      with_color_wrap.surround(io) do
        io << "world"
      end
      io << "bye"
    end
    io.to_s.should eq("\e[31mhello\e[39mworld\e[31mbye\e[39m")
  end

  it "colorizes with to_s" do
    colorize(ColorizeToS.new).red.to_s.should eq("\e[31mhello\e[39;34mworld\e[39;31mbye\e[39m")
  end

  it "toggles off" do
    colorize("hello").black.toggle(false).to_s.should eq("hello")
    colorize("hello").toggle(false).black.to_s.should eq("hello")
  end

  it "toggles off and on" do
    colorize("hello").toggle(false).black.toggle(true).to_s.should eq("\e[30mhello\e[39m")
  end

  it "colorizes nested strings" do
    colorize("hello #{colorize("foo").red} bar").underline.to_s.should eq("\e[4mhello \e[31mfoo\e[39m bar\e[24m")

    # TODO: Ideally this should work
    # colorize("hello #{colorize("foo").red} bar").green.to_s.should eq "\e[32mhello \e[39m\e[31mfoo\e[39m\e[32m bar\e[39m"
  end
end
