#
# Note:
#   mruby/c does not distinguish between singleton methods and class methods.
#   So, we need to use `STDOUT.print` instead of `print` in the IO class.
#

require 'env'

class IO
  def raw(&block)
    raw!
    res = block.call(self)
  ensure
    _restore_termios
    return res
  end

  def cooked(&block)
    cooked!
    res = block.call(self)
  ensure
    _restore_termios
    return res
  end

  def getch
    while true
      begin
        c = STDIN.read_nonblock(1)
      rescue Interrupt
        return "\x03"
      end
      next if c.nil?
      # @type var c: String
      return c
    end
    "" # unreachable. Just for steep
  end

  def self.get_cursor_position
    return [0, 0] if ENV['TERM'] == "dumb"
    row, col = 0, 0
    STDIN.read_nonblock(100) # discard buffer
    STDOUT.raw do
      STDOUT.print "\e[6n"
      while true
        c = STDIN.read_nonblock(1)&.ord || 0
        if 0x30 <= c && c <= 0x39 # "0".."9"
          row = row * 10 + c - 0x30
        elsif c == 0x3B # ";"
          break
        end
      end
      while true
        c = STDIN.read_nonblock(1)&.ord || 0
        # @type var c: Integer
        if 0x30 <= c && c <= 0x39
          col = col * 10 + c - 0x30
        elsif c == 0x52 # "R"
          break
        elsif c != 0
          raise "Invalid cursor position response"
        end
      end
    end
    if row == 0 || col == 0
      [24, 80] # default size
    else
      [row.to_i, col.to_i]
    end
  end

  def self.clear_screen
    STDOUT.print "\e[2J\e[1;1H"
  end

  def self.wait_terminal(timeout: 65535)
    res = ""
    STDIN.read_nonblock(100) # clear buffer
    STDOUT.print "\e[5n" # CSI DSR 5 to request terminal status report
    (timeout * 1000).to_i.times do |i|
      res << STDIN.read_nonblock(1).to_s
      break i if 3 < res.length
      sleep_ms 1
    end
    ENV['TERM'] = res.start_with?("\e[0n") ? "ansi" : "dumb"
  end
end
