///| Operating-system shims: blocking Winsock2 sockets, monotonic clock, entropy.

///| All FFI pointer parameters are `#borrow`, so MoonBit keeps ownership of the

///|
/// buffers and a `Bytes` can be re-sent or inspected after the call returns.
extern "C" fn net_init() -> Int = "kb_net_init"

///|
#borrow(host)
extern "C" fn raw_connect(host : Bytes, port : Int) -> Int64 = "kb_connect"

///|
extern "C" fn raw_close(sock : Int64) -> Unit = "kb_close"

///|
#borrow(data)
extern "C" fn raw_send(sock : Int64, data : Bytes, len : Int) -> Int = "kb_send_all"

///|
#borrow(data)
extern "C" fn raw_send_slice(
  sock : Int64,
  data : Bytes,
  offset : Int,
  len : Int,
) -> Int = "kb_send_slice"

///|
extern "C" fn raw_set_nodelay(sock : Int64, on : Int) -> Int = "kb_set_tcp_nodelay"

///|
extern "C" fn raw_set_send_buffer(sock : Int64, bytes : Int) -> Int = "kb_set_send_buffer"

///|
extern "C" fn raw_read(sock : Int64, n : Int) -> Bytes? = "kb_read_bytes"

///|
extern "C" fn raw_random(n : Int) -> Bytes = "kb_random_bytes"

///|
extern "C" fn raw_now_us() -> Int64 = "kb_now_us"

///|
extern "C" fn raw_sleep(ms : Int) -> Unit = "kb_sleep_ms"

///|
pub struct Socket(Int64)

///|
/// A socket-layer failure, carrying the message the caller should show.
pub struct IOError(String)

///| The message text of an IO error.

///|
/// This is how a caller renders one. `derive(Show)` became deprecated in the
/// toolchain this module is built with, and the promoted dot-methods of the other
/// derived traits were never part of the published interface, so the error text is
/// read through an explicit accessor instead.
pub fn IOError::message(self : IOError) -> String {
  self.0
}

///|
/// NUL-terminated C string as `Bytes`, since `String` has no stable C ABI.
pub fn cstr(s : String) -> Bytes {
  let buf = @buffer.Buffer()
  buf.write_string_utf8(s)
  buf.write_byte(b'\x00')
  buf.to_bytes()
}

///|
pub fn connect(host : String, port : Int) -> Result[Socket, IOError] {
  let _ = net_init()
  let h = raw_connect(cstr(host), port)
  if h < 0 {
    Err(IOError("connect \{host}:\{port} failed, winsock code \{h}"))
  } else {
    let _ = raw_set_nodelay(h, 1)
    Ok(Socket(h))
  }
}

///|
pub fn Socket::set_nodelay(self : Socket, on : Bool) -> Unit {
  let _ = raw_set_nodelay(self.0, if on { 1 } else { 0 })
}

///|
pub fn Socket::set_send_buffer(self : Socket, bytes : Int) -> Unit {
  let _ = raw_set_send_buffer(self.0, bytes)
}

///|
fn check_sent(sock : Int64, n : Int) -> Result[Unit, IOError] {
  if n < 0 {
    Err(IOError("send on socket \{sock} failed, winsock code \{n}"))
  } else {
    Ok(())
  }
}

///|
pub fn Socket::write(self : Socket, data : Bytes) -> Result[Unit, IOError] {
  check_sent(self.0, raw_send(self.0, data, data.length()))
}

///| Writes `len` bytes of `data` starting at `offset`, without copying.

///|
/// Lets a 5-byte message header and a large payload go out as two writes.
pub fn Socket::write_slice(
  self : Socket,
  data : Bytes,
  offset : Int,
  len : Int,
) -> Result[Unit, IOError] {
  check_sent(self.0, raw_send_slice(self.0, data, offset, len))
}

///|
/// Reads exactly `n` bytes into a fresh buffer.
pub fn Socket::read_exact(self : Socket, n : Int) -> Result[Bytes, IOError] {
  if n == 0 {
    return Ok(Bytes::make(0, b'\x00'))
  }
  match raw_read(self.0, n) {
    Some(b) => Ok(b)
    None => Err(IOError("connection closed while reading \{n} bytes"))
  }
}

///|
pub fn Socket::read_byte(self : Socket) -> Result[Byte, IOError] {
  match self.read_exact(1) {
    Ok(b) => Ok(b[0])
    Err(e) => Err(e)
  }
}

///|
pub fn Socket::read_int32_be(self : Socket) -> Result[Int, IOError] {
  match self.read_exact(4) {
    Ok(b) => Ok(be32(b, 0))
    Err(e) => Err(e)
  }
}

///| Reads one protocol message: tag byte, big-endian Int32 length (counting

///|
/// itself), then `length - 4` bytes of payload.
pub fn Socket::read_message(self : Socket) -> Result[Message, IOError] {
  let tag = match self.read_byte() {
    Ok(b) => b
    Err(e) => return Err(e)
  }
  let len = match self.read_int32_be() {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  if len < 4 {
    return Err(IOError("bad message length \{len}"))
  }
  let body = match self.read_exact(len - 4) {
    Ok(b) => b
    Err(e) => return Err(e)
  }
  Ok({ tag, body, })
}

///|
pub fn Socket::close(self : Socket) -> Unit {
  raw_close(self.0)
}

///|
/// A protocol message: tag plus payload.
pub struct Message {
  tag : Byte
  body : Bytes
}

///|
pub fn Message::len(self : Message) -> Int {
  self.body.length()
}

///|
/// UTF-8-decodes payload bytes from `offset` up to the next NUL byte.
pub fn Message::text_from(self : Message, offset : Int) -> String {
  text_at(self.body, offset)
}

///| UTF-8-decodes `length` bytes of `b` starting at `offset`.

///|

///| `Bytes::to_unchecked_string` must not be used here: on the native target it

///| reinterprets the byte range as UTF-16 code units, so ASCII protocol text

///|
/// comes back as CJK mojibake.
pub fn text_slice(b : Bytes, offset : Int, length : Int) -> String {
  @encoding/utf8.decode_lossy(b[offset:offset + length])
}

///| UTF-8-decodes `b` from `offset` up to (but excluding) the next NUL byte,

///|
/// which is how the protocol terminates strings and startup parameters.
pub fn text_at(b : Bytes, offset : Int) -> String {
  let n = b.length()
  let mut i = offset
  while i < n && b[i] != b'\x00' {
    i = i + 1
  }
  text_slice(b, offset, i - offset)
}

///| Text of everything written into `buf` so far.

///|

///| `Buffer::to_string` reinterprets the bytes as code units on the native

///|
/// target, which garbles ASCII output, so decode explicitly.
pub fn buffer_text(buf : @buffer.Buffer) -> String {
  let b = buf.to_bytes()
  text_slice(b, 0, b.length())
}

///|
pub fn be16(b : Bytes, off : Int) -> Int {
  (b[off].to_int() << 8) | b[off + 1].to_int()
}

///|
pub fn be32(b : Bytes, off : Int) -> Int {
  let x = b[off].to_int()
  let y = b[off + 1].to_int()
  let z = b[off + 2].to_int()
  let w = b[off + 3].to_int()
  (x << 24) | (y << 16) | (z << 8) | w
}

///|
pub fn be64(b : Bytes, off : Int) -> Int64 {
  let mut acc = 0L
  let mut i = 0
  while i < 8 {
    acc = (acc << 8) + b[off + i].to_int64()
    i = i + 1
  }
  acc
}

///|
pub fn random_bytes(n : Int) -> Bytes {
  raw_random(n)
}

///|
/// Monotonic clock in microseconds; the only clock used for timings.
pub fn now_us() -> Int64 {
  raw_now_us()
}

///|
pub fn sleep_ms(ms : Int) -> Unit {
  raw_sleep(ms)
}