module Network.TLS.Record.Layer (
    RecordLayer (..),
    newTransparentRecordLayer,
) where

import Network.TLS.Context
import Network.TLS.Imports
import Network.TLS.Record
import Network.TLS.Struct

import qualified Data.ByteString as B

newTransparentRecordLayer
    :: Eq ann
    => (Context -> IO ann)
    -> ([(ann, ByteString)] -> IO ())
    -> (Context -> IO (Either TLSError ByteString))
    -> RecordLayer [(ann, ByteString)]
newTransparentRecordLayer get send recv =
    RecordLayer
        { recordEncode = transparentEncodeRecord get
        , recordEncode13 = transparentEncodeRecord get
        , recordSendBytes = transparentSendBytes send
        , recordRecv = \ctx _ -> transparentRecvRecord recv ctx
        , recordRecv13 = transparentRecvRecord recv
        }

transparentEncodeRecord
    :: (Context -> IO ann)
    -> Context
    -> Record Plaintext
    -> IO (Either TLSError [(ann, ByteString)])
transparentEncodeRecord _ _ (Record ProtocolType_ChangeCipherSpec _ _) =
    return $ Right []
transparentEncodeRecord _ _ (Record ProtocolType_Alert _ _) =
    -- all alerts are silent and must be transported externally based on
    -- TLS exceptions raised by the library
    return $ Right []
transparentEncodeRecord get ctx (Record _ _ frag) =
    get ctx >>= \a -> return $ Right [(a, fragmentGetBytes frag)]

transparentSendBytes
    :: Eq ann
    => ([(ann, ByteString)] -> IO ())
    -> Context
    -> [(ann, ByteString)]
    -> IO ()
transparentSendBytes send _ input =
    send
        [ (a, bs) | (a, frgs) <- compress input, let bs = B.concat frgs, not (B.null bs)
        ]

transparentRecvRecord
    :: (Context -> IO (Either TLSError ByteString))
    -> Context
    -> IO (Either TLSError (Record Plaintext))
transparentRecvRecord recv ctx =
    fmap (Record ProtocolType_Handshake TLS12 . fragmentPlaintext) <$> recv ctx

compress :: Eq ann => [(ann, val)] -> [(ann, [val])]
compress [] = []
compress ((a, v) : xs) =
    let (ys, zs) = span ((== a) . fst) xs
     in (a, v : map snd ys) : compress zs
