content: {-# LANGUAGE RecordWildCards #-}
  
  module Hans.Tcp.Message where
  
  import Hans.Lens
  import Hans.Tcp.Packet
  import Hans.Tcp.Tcb
  
  import qualified Data.ByteString as S
  import           Data.IORef (readIORef)
  
  
  -- | @<SEQ=SEG.ACK><CTL=RST>@
  mkRst :: TcpHeader -> IO TcpHeader
  mkRst TcpHeader { .. } =
       return $ set tcpRst True
              $ emptyTcpHeader { tcpSourcePort = tcpDestPort
                               , tcpDestPort   = tcpSourcePort
                               , tcpSeqNum     = tcpAckNum
                               }
  {-# INLINE mkRst #-}
  
  
  -- | @<SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>@
  mkRstAck :: TcpHeader -> S.ByteString -> IO TcpHeader
  mkRstAck hdr @ TcpHeader { .. } payload =
       return $ set tcpRst True
              $ set tcpAck True
              $ emptyTcpHeader { tcpSourcePort = tcpDestPort
                               , tcpDestPort   = tcpSourcePort
                               , tcpSeqNum     = 0
                               , tcpAckNum     = tcpSegNextAckNum hdr (S.length payload)
                               }
  {-# INLINE mkRstAck #-}
  
  
  mkSyn :: Tcb -> IO TcpHeader
  mkSyn Tcb { .. } =
    do iss <- readIORef tcbIss
       return $ set tcpSyn True
              $ emptyTcpHeader { tcpSourcePort = tcbRemotePort
                               , tcpDestPort   = tcbLocalPort
                               , tcpSeqNum     = iss
                               , tcpAckNum     = 0
                               }
  {-# INLINE mkSyn #-}
  
  
  -- | @<SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>@
  mkSynAck :: Tcb -> TcpHeader -> IO TcpHeader
  mkSynAck Tcb { .. } TcpHeader { .. } =
    do iss <- readIORef tcbIss
       ack <- getRcvNxt tcbRecvWindow
  
       return $ set tcpSyn True
              $ set tcpAck True
              $ emptyTcpHeader { tcpSourcePort = tcpDestPort
                               , tcpDestPort   = tcpSourcePort
                               , tcpSeqNum     = iss
                               , tcpAckNum     = ack
                               }
  {-# INLINE mkSynAck #-}
  
  
  mkAck :: TcpSeqNum    -- ^ SEG.SEQ
        -> TcpSeqNum    -- ^ SEG.ACK
        -> TcpPort      -- ^ Local port
        -> TcpPort      -- ^ Remote port
        -> IO TcpHeader
  mkAck segSeq segAck local remote =
       return $ set tcpAck True
              $ emptyTcpHeader { tcpSourcePort = local
                               , tcpDestPort   = remote
                               , tcpSeqNum     = segSeq
                               , tcpAckNum     = segAck
                               }
  {-# INLINE mkAck #-}
  