# :nodoc:
class IO::ARGF < IO
  @path : String?
  @current_io : IO?

  def initialize(@argv : Array(String), @stdin : IO)
    @path = nil
    @current_io = nil
    @initialized = false
    @read_from_stdin = false
  end

  def read(slice : Bytes) : Int32
    first_initialize unless @initialized

    if current_io = @current_io
      read_count = read_from_current_io(current_io, slice)
    elsif !@read_from_stdin && !@argv.empty?
      # If there's no current_io it means we read all of ARGV.
      # It might be the case that the user put more strings into
      # ARGV, so in this case we need to read from that.
      read_next_argv
      read_count = read slice
    else
      read_count = 0
    end

    read_count.to_i32
  end

  # :nodoc:
  def peek : Bytes?
    first_initialize unless @initialized

    if current_io = @current_io
      peek = current_io.peek
      if peek && peek.empty? # EOF
        peek_next
      else
        peek
      end
    else
      peek_next
    end
  end

  private def peek_next
    if !@read_from_stdin && !@argv.empty?
      read_next_argv
      self.peek
    else
      nil
    end
  end

  def write(slice : Bytes) : NoReturn
    raise IO::Error.new "Can't write to ARGF"
  end

  def path : String
    @path || @argv.first? || "-"
  end

  private def first_initialize
    # This is the moment where we decide
    # whether we are going to use STDIN or ARGV
    @initialized = true
    if @argv.empty?
      @read_from_stdin = true
      @current_io = @stdin
    else
      read_next_argv
    end
  end

  private def read_from_current_io(current_io, slice)
    read_count = current_io.read slice
    if read_count.zero?
      unless @read_from_stdin
        current_io.close
        if @argv.empty?
          @current_io = nil
        else
          read_next_argv
          read_count = read slice
        end
      end
    end
    read_count
  end

  private def read_next_argv
    path = @path = @argv.shift
    @current_io = File.open(path)
  end
end
