///|
#cfg(platform="windows")
priv enum WindowsInput {
  ByteStream(@input.EventReader)
  Console(@win32.EventReader)
}

///|
#cfg(platform="windows")
fn WindowsInput::new(
  input_fd : @async/types.Fd,
  fallback : &@async/io.Reader,
) -> WindowsInput {
  if tty_isatty(input_fd) > 0 {
    Console(@win32.EventReader::new(input_fd))
  } else {
    ByteStream(@input.EventReader::new(fallback))
  }
}

///|
#cfg(platform="windows")
fn WindowsInput::close(self : WindowsInput) -> Unit {
  match self {
    ByteStream(_) => ()
    Console(reader) => reader.close()
  }
}

///|
#cfg(platform="windows")
async fn Tty::read_win32_console_event(
  self : Self,
  esc_timeout_ms : Int,
) -> Event {
  match self.input_backend {
    Console(reader) => self.read_win32_event(reader, esc_timeout_ms)
    ByteStream(reader) => read_decoded_input_event(reader, esc_timeout_ms)
  }
}

///|
#cfg(platform="windows")
async fn Tty::read_win32_event(
  self : Self,
  reader : @win32.EventReader,
  esc_timeout_ms : Int,
) -> Event {
  for ;; {
    match reader.read_event(esc_timeout_ms~) {
      Some(Input(event)) => return Input(event)
      Some(Resize) =>
        try self.window_size() catch {
          _ => ()
        } noraise {
          size => return Resize(size)
        }
      None => fail("Windows console input reader closed")
    }
  }
}

///|
#cfg(platform="windows")
async fn Tty::read_internal_event_from_sources(
  self : Self,
  esc_timeout_ms : Int,
) -> @input.Event {
  match self.input_backend {
    Console(reader) => reader.read_internal_event(esc_timeout_ms~)
    ByteStream(reader) => reader.read_event(esc_timeout_ms~)
  }
}

///|
#cfg(not(platform="windows"))
async fn Tty::read_internal_event_from_sources(
  self : Self,
  esc_timeout_ms : Int,
) -> @input.Event {
  self.reader.read_event(esc_timeout_ms~)
}