use core::{
    ffi::c_int,
    net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
};

use crate::{
    error::EResult,
    headers::sys_socket::{AF_INET, AF_INET6},
    util::PointerExt,
};

use super::sys_socket::{sa_family_t, socklen_t, SocketAddrExt, SOL_SOCKOPT_IPV6, SOL_SOCKOPT_TCP};

pub type in_port_t = u16;
pub type in_addr_t = u32;

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct in_addr {
    pub s_addr: in_addr_t,
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct in6_addr {
    pub s6_addr: [u8; 16],
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct sockaddr_in {
    pub sin_family: sa_family_t,
    pub sin_port: in_port_t,
    pub sin_addr: in_addr,
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct sockaddr_in6 {
    pub sin6_family: sa_family_t,
    pub sin6_port: in_port_t,
    pub sin6_flowinfo: u32,
    pub sin6_addr: in6_addr,
    pub sin6_scope_id: u32,
}

// TODO more IPv6

pub const IPPROTO_ICMP: c_int = 1;
pub const IPPROTO_TCP: c_int = 6;
pub const IPPROTO_UDP: c_int = 17;

pub const IPPROTO_IP: c_int = 0;
pub const IPPROTO_IPV6: c_int = 41;
pub const IPPROTO_RAW: c_int = 255;

pub const INADDR_ANY: in_addr_t = 0;
pub const INADDR_BROADCAST: in_addr_t = 0xFFFFFFFF;

pub const INET_ADDRSTRLEN: usize = 16;
pub const INET6_ADDRSTRLEN: usize = 46;

pub const IPV6_JOIN_GROUP: c_int = 1 | SOL_SOCKOPT_IPV6;
pub const IPV6_LEAVE_GROUP: c_int = 2 | SOL_SOCKOPT_IPV6;
pub const IPV6_MULTICAST_HOPS: c_int = 3 | SOL_SOCKOPT_IPV6;
pub const IPV6_MULTICAST_IF: c_int = 4 | SOL_SOCKOPT_IPV6;
pub const IPV6_MULTICAST_LOOP: c_int = 5 | SOL_SOCKOPT_IPV6;
pub const IPV6_UNICAST_HOPS: c_int = 6 | SOL_SOCKOPT_IPV6;
pub const IPV6_V6ONLY: c_int = 7 | SOL_SOCKOPT_IPV6;

pub const TCP_NODELAY: c_int = 1 | SOL_SOCKOPT_TCP;

impl in_addr {
    pub fn to_rust(&self) -> Ipv4Addr {
        Ipv4Addr::from(self.s_addr)
    }
}

impl in6_addr {
    pub fn to_rust(&self) -> Ipv6Addr {
        Ipv6Addr::from(self.s6_addr)
    }
}

impl sockaddr_in {
    pub unsafe fn to_rust(self: *const Self) -> EResult<SocketAddrV4> {
        let this = self.ensure();
        debug_assert_eq!(this.sin_family, AF_INET as u16);
        let ip = this.sin_addr.to_rust();
        let port = u16::from_be(this.sin_port);
        EResult::Ok(SocketAddrV4::new(ip, port))
    }
}

impl sockaddr_in6 {
    pub unsafe fn to_rust(self: *const Self) -> EResult<SocketAddrV6> {
        let this = self.ensure();
        debug_assert_eq!(this.sin6_family, AF_INET6 as u16);
        let ip = this.sin6_addr.to_rust();
        let port = u16::from_be(this.sin6_port);
        let flowinfo = u32::from_be(this.sin6_flowinfo);
        let scope_id = u32::from_be(this.sin6_scope_id);
        EResult::Ok(SocketAddrV6::new(ip, port, flowinfo, scope_id))
    }
}

impl SocketAddrExt<sockaddr_in> for SocketAddrV4 {
    fn to_c(&self, storage: *mut sockaddr_in, len: socklen_t) -> EResult<socklen_t> {
        let storage = unsafe { storage.ensure_mut() };
        debug_assert!(len >= size_of::<sockaddr_in>());
        storage.sin_family = AF_INET as u16;
        storage.sin_port = self.port().to_be();
        storage.sin_addr.s_addr = self.ip().to_bits().to_be();
        EResult::Ok(size_of::<sockaddr_in>())
    }
}

impl SocketAddrExt<sockaddr_in6> for SocketAddrV6 {
    fn to_c(&self, storage: *mut sockaddr_in6, len: socklen_t) -> EResult<socklen_t> {
        let storage = unsafe { storage.ensure_mut() };
        debug_assert!(len >= size_of::<sockaddr_in6>());
        storage.sin6_family = AF_INET6 as u16;
        storage.sin6_port = self.port().to_be();
        storage.sin6_addr.s6_addr = self.ip().octets();
        storage.sin6_flowinfo = self.flowinfo().to_be();
        storage.sin6_scope_id = self.scope_id().to_be();
        EResult::Ok(size_of::<sockaddr_in6>())
    }
}
