    -- | OSC over UDP implementation.
    module Sound.OSC.Transport.FD.UDP where

    import Control.Exception {- base -}
    import Control.Monad {- base -}
    import Data.Bifunctor {- base -}
    import qualified Network.Socket as N {- network -}
    import qualified Network.Socket.ByteString as C {- network -}

    import qualified Sound.OSC.Coding.Decode.Binary as Binary {- hosc -}
    import qualified Sound.OSC.Coding.Encode.Builder as Builder {- hosc -}
    import qualified Sound.OSC.Packet as Packet {- hosc -}
    import qualified Sound.OSC.Transport.FD as FD {- hosc -}

    -- | The UDP transport handle data type.
    data UDP = UDP {UDP -> Socket
    udpSocket :: N.Socket}

    -- | Return the port number associated with the UDP socket.
    udpPort :: Integral n => UDP -> IO n
    udpPort :: UDP -> IO n
    udpPort = (PortNumber -> n) -> IO PortNumber -> IO n
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap PortNumber -> n
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (IO PortNumber -> IO n) -> (UDP -> IO PortNumber) -> UDP -> IO n
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Socket -> IO PortNumber
    N.socketPort (Socket -> IO PortNumber)
    -> (UDP -> Socket) -> UDP -> IO PortNumber
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . UDP -> Socket
    udpSocket

    -- | Send packet over UDP.
    upd_send_packet :: UDP -> Packet.Packet -> IO ()
    upd_send_packet :: UDP -> Packet -> IO ()
    upd_send_packet (UDP fd :: Socket
    fd) p :: Packet
    p = IO Int -> IO ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (Socket -> ByteString -> IO Int
    C.send Socket
    fd (Packet -> ByteString
    Builder.encodePacket_strict Packet
    p))

    -- | Receive packet over UDP.
    udp_recv_packet :: UDP -> IO Packet.Packet
    udp_recv_packet :: UDP -> IO Packet
    udp_recv_packet (UDP fd :: Socket
    fd) = (ByteString -> Packet) -> IO ByteString -> IO Packet
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM ByteString -> Packet
    Binary.decodePacket_strict (Socket -> Int -> IO ByteString
    C.recv Socket
    fd 8192)

    -- | Close UDP.
    udp_close :: UDP -> IO ()
    udp_close :: UDP -> IO ()
    udp_close (UDP fd :: Socket
    fd) = Socket -> IO ()
    N.close Socket
    fd

    -- | 'UDP' is an instance of 'FD.Transport'.
    instance FD.Transport UDP where
       sendPacket :: UDP -> Packet -> IO ()
    sendPacket = UDP -> Packet -> IO ()
    upd_send_packet
       recvPacket :: UDP -> IO Packet
    recvPacket = UDP -> IO Packet
    udp_recv_packet
       close :: UDP -> IO ()
    close = UDP -> IO ()
    udp_close

    -- | Bracket UDP communication.
    with_udp :: IO UDP -> (UDP -> IO t) -> IO t
    with_udp :: IO UDP -> (UDP -> IO t) -> IO t
    with_udp u :: IO UDP
    u = IO UDP -> (UDP -> IO ()) -> (UDP -> IO t) -> IO t
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
    bracket IO UDP
    u UDP -> IO ()
    udp_close

    -- | Create and initialise UDP socket.
    udp_socket :: (N.Socket -> N.SockAddr -> IO ()) -> String -> Int -> IO UDP
    udp_socket :: (Socket -> SockAddr -> IO ()) -> String -> Int -> IO UDP
    udp_socket f :: Socket -> SockAddr -> IO ()
    f host :: String
    host port :: Int
    port = do
      Socket
    fd <- Family -> SocketType -> ProtocolNumber -> IO Socket
    N.socket Family
    N.AF_INET SocketType
    N.Datagram 0
      i :: AddrInfo
    i:_ <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
    N.getAddrInfo Maybe AddrInfo
    forall a. Maybe a
    Nothing (String -> Maybe String
    forall a. a -> Maybe a
    Just String
    host) (String -> Maybe String
    forall a. a -> Maybe a
    Just (Int -> String
    forall a. Show a => a -> String
    show Int
    port))
      let sa :: SockAddr
    sa = AddrInfo -> SockAddr
    N.addrAddress AddrInfo
    i
      Socket -> SockAddr -> IO ()
    f Socket
    fd SockAddr
    sa
      UDP -> IO UDP
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Socket -> UDP
    UDP Socket
    fd)

    -- | Set option, ie. 'N.Broadcast' or 'N.RecvTimeOut'.
    set_udp_opt :: N.SocketOption -> Int -> UDP -> IO ()
    set_udp_opt :: SocketOption -> Int -> UDP -> IO ()
    set_udp_opt k :: SocketOption
    k v :: Int
    v (UDP s :: Socket
    s) = Socket -> SocketOption -> Int -> IO ()
    N.setSocketOption Socket
    s SocketOption
    k Int
    v

    -- | Get option.
    get_udp_opt :: N.SocketOption -> UDP -> IO Int
    get_udp_opt :: SocketOption -> UDP -> IO Int
    get_udp_opt k :: SocketOption
    k (UDP s :: Socket
    s) = Socket -> SocketOption -> IO Int
    N.getSocketOption Socket
    s SocketOption
    k

    -- | Make a 'UDP' connection.
    openUDP :: String -> Int -> IO UDP
    openUDP :: String -> Int -> IO UDP
    openUDP = (Socket -> SockAddr -> IO ()) -> String -> Int -> IO UDP
    udp_socket Socket -> SockAddr -> IO ()
    N.connect

    {- | Trivial 'UDP' server socket.

    > import Control.Concurrent {- base -}

    > let t0 = udpServer "127.0.0.1" 57300
    > forkIO (FD.withTransport t0 (\fd -> forever (FD.recvMessage fd >>= print)))

    > let t1 = openUDP "127.0.0.1" 57300
    > FD.withTransport t1 (\fd -> FD.sendMessage fd (Packet.message "/n" []))
    -}
    udpServer :: String -> Int -> IO UDP
    udpServer :: String -> Int -> IO UDP
    udpServer = (Socket -> SockAddr -> IO ()) -> String -> Int -> IO UDP
    udp_socket Socket -> SockAddr -> IO ()
    N.bind

    -- | Variant of 'udpServer' that doesn't require the host address.
    udp_server :: Int -> IO UDP
    udp_server :: Int -> IO UDP
    udp_server p :: Int
    p = do
      let hints :: AddrInfo
    hints =
            AddrInfo
    N.defaultHints
            {addrFlags :: [AddrInfoFlag]
    N.addrFlags = [AddrInfoFlag
    N.AI_PASSIVE,AddrInfoFlag
    N.AI_NUMERICSERV]
            ,addrSocketType :: SocketType
    N.addrSocketType = SocketType
    N.Datagram}
      a :: AddrInfo
    a:_ <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
    N.getAddrInfo (AddrInfo -> Maybe AddrInfo
    forall a. a -> Maybe a
    Just AddrInfo
    hints) Maybe String
    forall a. Maybe a
    Nothing (String -> Maybe String
    forall a. a -> Maybe a
    Just (Int -> String
    forall a. Show a => a -> String
    show Int
    p))
      Socket
    s <- Family -> SocketType -> ProtocolNumber -> IO Socket
    N.socket (AddrInfo -> Family
    N.addrFamily AddrInfo
    a) (AddrInfo -> SocketType
    N.addrSocketType AddrInfo
    a) (AddrInfo -> ProtocolNumber
    N.addrProtocol AddrInfo
    a)
      Socket -> SocketOption -> Int -> IO ()
    N.setSocketOption Socket
    s SocketOption
    N.ReuseAddr 1
      Socket -> SockAddr -> IO ()
    N.bind Socket
    s (AddrInfo -> SockAddr
    N.addrAddress AddrInfo
    a)
      UDP -> IO UDP
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Socket -> UDP
    UDP Socket
    s)

    -- | Send variant to send to specified address.
    sendTo :: UDP -> Packet.Packet -> N.SockAddr -> IO ()
    sendTo :: UDP -> Packet -> SockAddr -> IO ()
    sendTo (UDP fd :: Socket
    fd) p :: Packet
    p = IO Int -> IO ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (IO Int -> IO ()) -> (SockAddr -> IO Int) -> SockAddr -> IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Socket -> ByteString -> SockAddr -> IO Int
    C.sendTo Socket
    fd (Packet -> ByteString
    Builder.encodePacket_strict Packet
    p)

    -- | Recv variant to collect message source address.
    recvFrom :: UDP -> IO (Packet.Packet, N.SockAddr)
    recvFrom :: UDP -> IO (Packet, SockAddr)
    recvFrom (UDP fd :: Socket
    fd) = ((ByteString, SockAddr) -> (Packet, SockAddr))
    -> IO (ByteString, SockAddr) -> IO (Packet, SockAddr)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((ByteString -> Packet)
    -> (ByteString, SockAddr) -> (Packet, SockAddr)
    forall (p :: * -> * -> *) a b c.
    Bifunctor p =>
    (a -> b) -> p a c -> p b c
    first ByteString -> Packet
    Binary.decodePacket_strict) (Socket -> Int -> IO (ByteString, SockAddr)
    C.recvFrom Socket
    fd 8192)