-- | Osc over Tcp implementation.
module Sound.Osc.Transport.Fd.Tcp where

import qualified Control.Exception as Exception {- base -}
import qualified Data.ByteString.Lazy as ByteString.Lazy {- bytestring -}
import qualified Network.Socket as Socket {- network -}
import qualified System.IO as Io {- base -}

import qualified Sound.Osc.Coding.Byte as Byte {- hosc -}
import qualified Sound.Osc.Coding.Convert as Convert {- hosc -}
import qualified Sound.Osc.Coding.Decode.Binary as Decode.Binary {- hosc -}
import qualified Sound.Osc.Coding.Encode.Builder as Encode.Builder {- hosc -}
import qualified Sound.Osc.Packet as Packet {- hosc -}
import qualified Sound.Osc.Transport.Fd as Fd {- hosc -}

-- | The Tcp transport handle data type.
newtype Tcp = Tcp {Tcp -> Handle
tcpHandle :: Io.Handle}

-- | Send data over Tcp.
tcp_send_data :: Tcp -> ByteString.Lazy.ByteString -> IO ()
tcp_send_data :: Tcp -> ByteString -> IO ()
tcp_send_data (Tcp Handle
fd) ByteString
d = do
  let n :: Word32
n = Int64 -> Word32
Convert.int64_to_word32 (ByteString -> Int64
ByteString.Lazy.length ByteString
d)
  Handle -> ByteString -> IO ()
ByteString.Lazy.hPut Handle
fd (ByteString -> ByteString -> ByteString
ByteString.Lazy.append (Word32 -> ByteString
Byte.encode_word32 Word32
n) ByteString
d)
  Handle -> IO ()
Io.hFlush Handle
fd

-- | Send packet over Tcp.
tcp_send_packet :: Tcp -> Packet.PacketOf Packet.Message -> IO ()
tcp_send_packet :: Tcp -> PacketOf Message -> IO ()
tcp_send_packet Tcp
tcp PacketOf Message
p = Tcp -> ByteString -> IO ()
tcp_send_data Tcp
tcp (PacketOf Message -> ByteString
Encode.Builder.encodePacket PacketOf Message
p)

-- | Receive packet over Tcp.
tcp_recv_packet :: Tcp -> IO (Packet.PacketOf Packet.Message)
tcp_recv_packet :: Tcp -> IO (PacketOf Message)
tcp_recv_packet (Tcp Handle
fd) = do
  ByteString
b0 <- Handle -> Int -> IO ByteString
ByteString.Lazy.hGet Handle
fd Int
4
  ByteString
b1 <- Handle -> Int -> IO ByteString
ByteString.Lazy.hGet Handle
fd (Word32 -> Int
Convert.word32_to_int (ByteString -> Word32
Byte.decode_word32 ByteString
b0))
  PacketOf Message -> IO (PacketOf Message)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> PacketOf Message
Decode.Binary.decodePacket ByteString
b1)

tcp_recv_packet_or :: Tcp -> IO (Either String Packet.Packet)
tcp_recv_packet_or :: Tcp -> IO (Either String (PacketOf Message))
tcp_recv_packet_or (Tcp Handle
fd) = do
  ByteString
b0 <- Handle -> Int -> IO ByteString
ByteString.Lazy.hGet Handle
fd Int
4
  ByteString
b1 <- Handle -> Int -> IO ByteString
ByteString.Lazy.hGet Handle
fd (Word32 -> Int
Convert.word32_to_int (ByteString -> Word32
Byte.decode_word32 ByteString
b0))
  Either String (PacketOf Message)
-> IO (Either String (PacketOf Message))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Either String (PacketOf Message)
Decode.Binary.decodePacketOr ByteString
b1)

-- | Close Tcp.
tcp_close :: Tcp -> IO ()
tcp_close :: Tcp -> IO ()
tcp_close = Handle -> IO ()
Io.hClose (Handle -> IO ()) -> (Tcp -> Handle) -> Tcp -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Tcp -> Handle
tcpHandle

-- | 'Tcp' is an instance of 'Transport'.
instance Fd.Transport Tcp where
  sendPacket :: Tcp -> PacketOf Message -> IO ()
sendPacket = Tcp -> PacketOf Message -> IO ()
tcp_send_packet
  recvPacket :: Tcp -> IO (PacketOf Message)
recvPacket = Tcp -> IO (PacketOf Message)
tcp_recv_packet
  recvPacketOr :: Tcp -> IO (Either String (PacketOf Message))
recvPacketOr = Tcp -> IO (Either String (PacketOf Message))
tcp_recv_packet_or
  close :: Tcp -> IO ()
close = Tcp -> IO ()
tcp_close

-- | Bracket Tcp communication.
with_tcp :: IO Tcp -> (Tcp -> IO t) -> IO t
with_tcp :: forall t. IO Tcp -> (Tcp -> IO t) -> IO t
with_tcp IO Tcp
u = IO Tcp -> (Tcp -> IO ()) -> (Tcp -> IO t) -> IO t
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Exception.bracket IO Tcp
u Tcp -> IO ()
tcp_close

-- | Create and initialise Tcp socket.
tcp_socket :: (Socket.Socket -> Socket.SockAddr -> IO ()) -> Maybe String -> Int -> IO Socket.Socket
tcp_socket :: (Socket -> SockAddr -> IO ()) -> Maybe String -> Int -> IO Socket
tcp_socket Socket -> SockAddr -> IO ()
f Maybe String
host Int
port = do
  Socket
fd <- Family -> SocketType -> ProtocolNumber -> IO Socket
Socket.socket Family
Socket.AF_INET SocketType
Socket.Stream ProtocolNumber
0
  let hints :: AddrInfo
hints = AddrInfo
Socket.defaultHints {Socket.addrFamily = Socket.AF_INET} -- localhost=ipv4
  AddrInfo
i : [AddrInfo]
_ <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
forall (t :: * -> *).
GetAddrInfo t =>
Maybe AddrInfo -> Maybe String -> Maybe String -> IO (t AddrInfo)
Socket.getAddrInfo (AddrInfo -> Maybe AddrInfo
forall a. a -> Maybe a
Just AddrInfo
hints) Maybe 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
Socket.addrAddress AddrInfo
i
  ()
_ <- Socket -> SockAddr -> IO ()
f Socket
fd SockAddr
sa
  Socket -> IO Socket
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Socket
fd

-- | Convert 'Socket.Socket' to 'Tcp'.
socket_to_tcp :: Socket.Socket -> IO Tcp
socket_to_tcp :: Socket -> IO Tcp
socket_to_tcp Socket
fd = (Handle -> Tcp) -> IO Handle -> IO Tcp
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Handle -> Tcp
Tcp (Socket -> IOMode -> IO Handle
Socket.socketToHandle Socket
fd IOMode
Io.ReadWriteMode)

-- | Create and initialise Tcp.
tcp_handle :: (Socket.Socket -> Socket.SockAddr -> IO ()) -> String -> Int -> IO Tcp
tcp_handle :: (Socket -> SockAddr -> IO ()) -> String -> Int -> IO Tcp
tcp_handle Socket -> SockAddr -> IO ()
f String
host Int
port = (Socket -> SockAddr -> IO ()) -> Maybe String -> Int -> IO Socket
tcp_socket Socket -> SockAddr -> IO ()
f (String -> Maybe String
forall a. a -> Maybe a
Just String
host) Int
port IO Socket -> (Socket -> IO Tcp) -> IO Tcp
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Socket -> IO Tcp
socket_to_tcp

{- | Make a 'Tcp' connection.

> import Sound.Osc.Datum
> import Sound.Osc.Time
> let t = openTcp "127.0.0.1" 57110
> let m1 = Packet.message "/dumpOsc" [Int32 1]
> let m2 = Packet.message "/g_new" [Int32 1]
> Fd.withTransport t (\fd -> let f = Fd.sendMessage fd in f m1 >> pauseThread 0.25 >> f m2)
-}
openTcp :: String -> Int -> IO Tcp
openTcp :: String -> Int -> IO Tcp
openTcp = (Socket -> SockAddr -> IO ()) -> String -> Int -> IO Tcp
tcp_handle Socket -> SockAddr -> IO ()
Socket.connect

-- | 'Socket.accept' connection at /s/ and run /f/.
tcp_server_f :: Socket.Socket -> (Tcp -> IO ()) -> IO ()
tcp_server_f :: Socket -> (Tcp -> IO ()) -> IO ()
tcp_server_f Socket
s Tcp -> IO ()
f = do
  (Socket
fd, SockAddr
_) <- Socket -> IO (Socket, SockAddr)
Socket.accept Socket
s
  Tcp
h <- Socket -> IO Tcp
socket_to_tcp Socket
fd
  Tcp -> IO ()
f Tcp
h

-- | A trivial 'Tcp' /Osc/ server.
tcp_server :: Int -> (Tcp -> IO ()) -> IO ()
tcp_server :: Int -> (Tcp -> IO ()) -> IO ()
tcp_server Int
port Tcp -> IO ()
f = do
  Socket
s <- (Socket -> SockAddr -> IO ()) -> Maybe String -> Int -> IO Socket
tcp_socket Socket -> SockAddr -> IO ()
Socket.bind Maybe String
forall a. Maybe a
Nothing Int
port
  Socket -> Int -> IO ()
Socket.listen Socket
s Int
1
  let repeatM_ :: IO a -> IO ()
repeatM_ = [IO a] -> IO ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ ([IO a] -> IO ()) -> (IO a -> [IO a]) -> IO a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO a -> [IO a]
forall a. a -> [a]
repeat
  IO () -> IO ()
forall {a}. IO a -> IO ()
repeatM_ (Socket -> (Tcp -> IO ()) -> IO ()
tcp_server_f Socket
s Tcp -> IO ()
f)