{-# LANGUAGE EmptyDataDecls #-}

-- | The Record Protocol takes messages to be transmitted, fragments
-- the data into manageable blocks, optionally compresses the data,
-- applies a MAC, encrypts, and transmits the result.  Received data
-- is decrypted, verified, decompressed, reassembled, and then
-- delivered to higher-level clients.
module Network.TLS.Record.Types (
    Header (..),
    ProtocolType (..),
    packetType,

    -- * TLS Records
    Record (..),

    -- * TLS Record fragment and constructors
    Fragment,
    fragmentGetBytes,
    fragmentPlaintext,
    fragmentCompressed,
    fragmentCiphertext,
    Plaintext,
    Compressed,
    Ciphertext,

    -- * manipulate record
    onRecordFragment,
    fragmentCompress,
    fragmentCipher,
    fragmentUncipher,
    fragmentUncompress,

    -- * serialize record
    rawToRecord,
    recordToRaw,
    recordToHeader,
) where

import qualified Data.ByteString as B
import Network.TLS.Imports
import Network.TLS.Record.State
import Network.TLS.Struct

-- | Represent a TLS record.
data Record a = Record ProtocolType Version (Fragment a) deriving (Show, Eq)

newtype Fragment a = Fragment {fragmentGetBytes :: ByteString}
    deriving (Show, Eq)

data Plaintext
data Compressed
data Ciphertext

fragmentPlaintext :: ByteString -> Fragment Plaintext
fragmentPlaintext bytes = Fragment bytes

fragmentCompressed :: ByteString -> Fragment Compressed
fragmentCompressed bytes = Fragment bytes

fragmentCiphertext :: ByteString -> Fragment Ciphertext
fragmentCiphertext bytes = Fragment bytes

onRecordFragment
    :: Record a -> (Fragment a -> RecordM (Fragment b)) -> RecordM (Record b)
onRecordFragment (Record pt ver frag) f = Record pt ver <$> f frag

fragmentMap
    :: (ByteString -> RecordM ByteString) -> Fragment a -> RecordM (Fragment b)
fragmentMap f (Fragment b) = Fragment <$> f b

-- | turn a plaintext record into a compressed record using the compression function supplied
fragmentCompress
    :: (ByteString -> RecordM ByteString)
    -> Fragment Plaintext
    -> RecordM (Fragment Compressed)
fragmentCompress f = fragmentMap f

-- | turn a compressed record into a ciphertext record using the cipher function supplied
fragmentCipher
    :: (ByteString -> RecordM ByteString)
    -> Fragment Compressed
    -> RecordM (Fragment Ciphertext)
fragmentCipher f = fragmentMap f

-- | turn a ciphertext fragment into a compressed fragment using the cipher function supplied
fragmentUncipher
    :: (ByteString -> RecordM ByteString)
    -> Fragment Ciphertext
    -> RecordM (Fragment Compressed)
fragmentUncipher f = fragmentMap f

-- | turn a compressed fragment into a plaintext fragment using the decompression function supplied
fragmentUncompress
    :: (ByteString -> RecordM ByteString)
    -> Fragment Compressed
    -> RecordM (Fragment Plaintext)
fragmentUncompress f = fragmentMap f

-- | turn a record into an header and bytes
recordToRaw :: Record a -> (Header, ByteString)
recordToRaw (Record pt ver (Fragment bytes)) = (Header pt ver (fromIntegral $ B.length bytes), bytes)

-- | turn a header and a fragment into a record
rawToRecord :: Header -> Fragment a -> Record a
rawToRecord (Header pt ver _) fragment = Record pt ver fragment

-- | turn a record into a header
recordToHeader :: Record a -> Header
recordToHeader (Record pt ver (Fragment bytes)) = Header pt ver (fromIntegral $ B.length bytes)
