// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
extern "c" fn uv_IF_NAMESIZE() -> Int = "moonbit_uv_IF_NAMESIZE"

///|
let _IF_NAMESIZE : Int = uv_IF_NAMESIZE()

///|
struct IfIndex(UInt)

///|
pub impl Show for IfIndex with output(self : IfIndex, logger : &Logger) -> Unit {
  logger.write_string(self.0.to_string())
}

///|
pub impl ToJson for IfIndex with to_json(self : IfIndex) -> Json {
  return self.0.to_json()
}

///|
#borrow(name, size)
extern "c" fn uv_if_indextoname(
  index : UInt,
  name : FixedArray[Byte],
  size : Ref[Int],
) -> Int = "moonbit_uv_if_indextoname"

///|
pub fn IfIndex::to_name(self : IfIndex) -> Bytes raise {
  let mut name : FixedArray[Byte] = FixedArray::make(_IF_NAMESIZE, 0)
  let size : Ref[Int] = Ref::new(name.length())
  let mut status = uv_if_indextoname(self.0, name, size)
  if status == _ENOBUFS {
    name = FixedArray::make(size.val, 0)
    status = uv_if_indextoname(self.0, name, size)
  }
  if status < 0 {
    raise Errno::of_int(status)
  }
  let buf : FixedArray[Byte] = FixedArray::make(size.val, 0)
  buf.unsafe_blit(0, name, 0, size.val)
  return buf.unsafe_reinterpret_as_bytes()
}

///|
#borrow(iid)
extern "c" fn uv_if_indextoiid(index : UInt, iid : FixedArray[Byte]) -> Int = "moonbit_uv_if_indextoiid"

///|
pub fn IfIndex::to_iid(self : IfIndex) -> Bytes raise {
  let mut iid : FixedArray[Byte] = FixedArray::make(16, 0)
  let mut status = uv_if_indextoiid(self.0, iid)
  if status == _ENOBUFS {
    iid = FixedArray::make(status, 0)
    status = uv_if_indextoiid(self.0, iid)
  }
  if status < 0 {
    raise Errno::of_int(status)
  }
  let buf : FixedArray[Byte] = FixedArray::make(status, 0)
  buf.unsafe_blit(0, iid, 0, status)
  return buf.unsafe_reinterpret_as_bytes()
}

///|
struct PhysAddr(UInt64)

///|
pub impl Show for PhysAddr with output(self : PhysAddr, logger : &Logger) -> Unit {
  let b0 = (self.0 >> 40) & 0xff
  let b1 = (self.0 >> 32) & 0xff
  let b2 = (self.0 >> 24) & 0xff
  let b3 = (self.0 >> 16) & 0xff
  let b4 = (self.0 >> 8) & 0xff
  let b5 = self.0 & 0xff
  logger.write_string(b0.to_string(radix=16).pad_start(2, '0'))
  logger.write_char(':')
  logger.write_string(b1.to_string(radix=16).pad_start(2, '0'))
  logger.write_char(':')
  logger.write_string(b2.to_string(radix=16).pad_start(2, '0'))
  logger.write_char(':')
  logger.write_string(b3.to_string(radix=16).pad_start(2, '0'))
  logger.write_char(':')
  logger.write_string(b4.to_string(radix=16).pad_start(2, '0'))
  logger.write_char(':')
  logger.write_string(b5.to_string(radix=16).pad_start(2, '0'))
}

///|
pub impl ToJson for PhysAddr with to_json(self : PhysAddr) -> Json {
  return self.to_string().to_json()
}

///|
pub struct InterfaceAddress {
  name : Bytes
  phys_addr : PhysAddr
  is_internal : Bool
  address : Sockaddr
  netmask : Sockaddr
} derive(ToJson)

///|
extern "c" fn uv_interface_address_sizeof() -> UInt64 = "moonbit_uv_interface_address_sizeof"

///|
impl @sizeof.Sized for InterfaceAddress with size() -> @c.Size {
  uv_interface_address_sizeof()
}

///|
extern "c" fn uv_interface_address_name(
  address : @c.Pointer[InterfaceAddress],
) -> Bytes = "moonbit_uv_interface_address_name"

///|
extern "c" fn uv_interface_address_phys_addr(
  address : @c.Pointer[InterfaceAddress],
) -> UInt64 = "moonbit_uv_interface_address_phys_addr"

///|
extern "c" fn uv_interface_address_is_internal(
  address : @c.Pointer[InterfaceAddress],
) -> Int = "moonbit_uv_interface_address_is_internal"

///|
extern "c" fn uv_interface_address_address(
  address : @c.Pointer[InterfaceAddress],
) -> Sockaddr = "moonbit_uv_interface_address_address"

///|
extern "c" fn uv_interface_address_netmask(
  address : @c.Pointer[InterfaceAddress],
) -> Sockaddr = "moonbit_uv_interface_address_netmask"

///|
impl @pointer.Load for InterfaceAddress with load(
  self : @c.Pointer[InterfaceAddress],
) -> InterfaceAddress {
  return InterfaceAddress::{
    name: uv_interface_address_name(self),
    phys_addr: uv_interface_address_phys_addr(self),
    is_internal: uv_interface_address_is_internal(self) != 0,
    address: uv_interface_address_address(self),
    netmask: uv_interface_address_netmask(self),
  }
}

///|
#owned(addresses, count)
extern "c" fn uv_interface_addresses(
  addresses : Ref[@c.Pointer[InterfaceAddress]],
  count : Ref[Int],
) -> Int = "moonbit_uv_interface_addresses"

///|
extern "c" fn uv_free_interface_addresses(
  addresses : @c.Pointer[InterfaceAddress],
  count : Int,
) = "moonbit_uv_free_interface_addresses"

///|
pub fn interface_addresses() -> FixedArray[InterfaceAddress] raise {
  let addresses = Ref::new(@c.Pointer::null())
  let count : Ref[Int] = Ref::new(0)
  let status = uv_interface_addresses(addresses, count)
  if status < 0 {
    raise Errno::of_int(status)
  }
  let result = FixedArray::makei(count.val, i => addresses.val[i])
  uv_free_interface_addresses(addresses.val, count.val)
  return result
}