{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE OverloadedStrings #-}

module Network.QUIC.Types.Frame where

import Network.QUIC.Imports
import Network.QUIC.Types.Ack
import Network.QUIC.Types.CID
import Network.QUIC.Types.Error
import Network.QUIC.Types.Time

----------------------------------------------------------------

type FrameType = Int

data Direction = Unidirectional | Bidirectional deriving (Eq, Show)

type ReasonPhrase = ShortByteString
type SeqNum = Int

data Frame
    = Padding Int
    | Ping
    | Ack AckInfo Delay
    | ResetStream StreamId ApplicationProtocolError Int
    | StopSending StreamId ApplicationProtocolError
    | CryptoF Offset CryptoData
    | NewToken Token
    | StreamF StreamId Offset [StreamData] Fin
    | MaxData Int
    | MaxStreamData StreamId Int
    | MaxStreams Direction Int
    | DataBlocked Int
    | StreamDataBlocked StreamId Int
    | StreamsBlocked Direction Int
    | NewConnectionID CIDInfo SeqNum -- retire prior to
    | RetireConnectionID SeqNum
    | PathChallenge PathData
    | PathResponse PathData
    | ConnectionClose TransportError FrameType ReasonPhrase
    | ConnectionCloseApp ApplicationProtocolError ReasonPhrase
    | HandshakeDone
    | UnknownFrame Int
    deriving (Eq, Show)

-- | Stream identifier.
--   This should be 62-bit interger.
--   On 32-bit machines, the total number of stream identifiers is limited.
type StreamId = Int

-- | Checking if a stream is client-initiated bidirectional.
isClientInitiatedBidirectional :: StreamId -> Bool
isClientInitiatedBidirectional sid = (0b11 .&. sid) == 0

-- | Checking if a stream is server-initiated bidirectional.
isServerInitiatedBidirectional :: StreamId -> Bool
isServerInitiatedBidirectional sid = (0b11 .&. sid) == 1

-- | Checking if a stream is client-initiated unidirectional.
isClientInitiatedUnidirectional :: StreamId -> Bool
isClientInitiatedUnidirectional sid = (0b11 .&. sid) == 2

-- | Checking if a stream is server-initiated unidirectional.
isServerInitiatedUnidirectional :: StreamId -> Bool
isServerInitiatedUnidirectional sid = (0b11 .&. sid) == 3

isClientInitiated :: StreamId -> Bool
isClientInitiated sid = (0b1 .&. sid) == 0

isServerInitiated :: StreamId -> Bool
isServerInitiated sid = (0b1 .&. sid) == 1

isBidirectional :: StreamId -> Bool
isBidirectional sid = (0b10 .&. sid) == 0

isUnidirectional :: StreamId -> Bool
isUnidirectional sid = (0b10 .&. sid) == 2

type Delay = Milliseconds

type Fin = Bool

type CryptoData = ByteString
type StreamData = ByteString

type Token = ByteString -- to be decrypted
emptyToken :: Token
emptyToken = ""

{- FOURMOLU_DISABLE -}
ackEliciting :: Frame -> Bool
ackEliciting Padding{}            = False
ackEliciting Ack{}                = False
ackEliciting ConnectionClose{}    = False
ackEliciting ConnectionCloseApp{} = False
ackEliciting _                    = True

pathValidating :: Frame -> Bool
pathValidating PathChallenge{} = True
pathValidating PathResponse{}  = True
pathValidating _               = False

inFlight :: Frame -> Bool
inFlight Ack{}                = False
inFlight ConnectionClose{}    = False
inFlight ConnectionCloseApp{} = False
inFlight _                    = True

rateControled :: Frame -> Bool
rateControled ResetStream{}        = True
rateControled StopSending{}        = True
rateControled PathChallenge{}      = True
rateControled PathResponse{}       = True
rateControled NewConnectionID{}    = True
rateControled RetireConnectionID{} = True
rateControled _                    = False
{- FOURMOLU_ENABLE -}
