require "../src/reply"
require "crystal/syntax_highlighter/colorize"
require "compiler/crystal/tools/formatter"

CRYSTAL_KEYWORD = %w(
  abstract alias annotation asm begin break case class
  def do else elsif end ensure enum extend for fun
  if in include instance_sizeof lib macro module
  next of offsetof out pointerof private protected require
  rescue return select sizeof struct super
  then type typeof union uninitialized unless until
  verbatim when while with yield
)

CONTINUE_ERROR = [
  "expecting identifier 'end', not 'EOF'",
  "expecting token 'CONST', not 'EOF'",
  "expecting any of these tokens: IDENT, CONST, `, <<, <, <=, ==, ===, !=, =~, !~, >>, >, >=, +, -, *, /, //, !, ~, %, &, |, ^, **, [], []?, []=, <=>, &+, &-, &*, &** (not 'EOF')",
  "expecting any of these tokens: ;, NEWLINE (not 'EOF')",
  "expecting token ')', not 'EOF'",
  "expecting token ']', not 'EOF'",
  "expecting token '}', not 'EOF'",
  "expecting token '%}', not 'EOF'",
  "expecting token '}', not ','",
  "expected '}' or named tuple name, not EOF",
  "unexpected token: NEWLINE",
  "unexpected token: EOF",
  "unexpected token: EOF (expecting when, else or end)",
  "unexpected token: EOF (expecting ',', ';' or '\n')",
  "Unexpected EOF on heredoc identifier",
  "unterminated parenthesized expression",
  "unterminated call",
  "Unterminated string literal",
  "unterminated hash literal",
  "Unterminated command literal",
  "unterminated array literal",
  "unterminated tuple literal",
  "unterminated macro",
  "Unterminated string interpolation",
  "invalid trailing comma in call",
  "unknown token: '\\u{0}'",
]

# `"`, `:`, `'`, are not a delimiter because symbols and strings are treated as one word.
# '=', !', '?' are not a delimiter because they could make part of method name.
WORD_DELIMITERS = {{" \n\t+-*/,;@&%<>^\\[](){}|.~".chars}}

class CrystalReader < Reply::Reader
  def prompt(io : IO, line_number : Int32, color : Bool) : Nil
    io << "crystal".colorize.blue.toggle(color)
    io << ':'
    io << sprintf("%03d", line_number)
    io << "> "
  end

  def highlight(expression : String) : String
    Crystal::SyntaxHighlighter::Colorize.highlight!(expression)
  end

  def continue?(expression : String) : Bool
    Crystal::Parser.new(expression).parse
    false
  rescue e : Crystal::CodeError
    e.message.in? CONTINUE_ERROR
  end

  def format(expression : String) : String?
    Crystal.format(expression).chomp rescue nil
  end

  def indentation_level(expression_before_cursor : String) : Int32?
    parser = Crystal::Parser.new(expression_before_cursor)
    parser.parse rescue nil

    parser.type_nest + parser.def_nest + parser.fun_nest
  end

  def reindent_line(line)
    case line.strip
    when "end", ")", "]", "}"
      0
    when "else", "elsif", "rescue", "ensure", "in", "when"
      -1
    else
      nil
    end
  end

  def save_in_history?(expression : String) : Bool
    !expression.blank?
  end

  def history_file : Path | String | IO | Nil
    "history.txt"
  end

  def auto_complete(name_filter : String, expression : String) : {String, Array(String)}
    return "Keywords:", CRYSTAL_KEYWORD.dup
  end

  def auto_completion_display_title(io : IO, title : String)
    io << title
  end

  def auto_completion_display_selected_entry(io : IO, entry : String)
    io << entry.colorize.red.bright
  end

  def auto_completion_display_entry(io : IO, entry_matched : String, entry_remaining : String)
    io << entry_matched.colorize.red.bright << entry_remaining
  end
end

reader = CrystalReader.new
reader.word_delimiters = WORD_DELIMITERS

reader.read_loop do |expression|
  case expression
  when "clear_history"
    reader.clear_history
  when "reset"
    reader.reset
  when "exit"
    break
  when .presence
    # Eval expression here
    print " => "
    puts Crystal::SyntaxHighlighter::Colorize.highlight!(expression)
  end
end
