module Network.HPACK.HeaderBlock.Encode (
    toByteStream
  ) where

import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder as BB
import Data.Bits (setBit)
import Data.List (foldl')
import Data.Monoid ((<>), mempty)
import Data.Word (Word8)
import Network.HPACK.HeaderBlock.HeaderField
import qualified Network.HPACK.HeaderBlock.Integer as I
import qualified Network.HPACK.HeaderBlock.String as S
import Network.HPACK.Huffman
import Network.HPACK.Types

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

-- | Converting 'HeaderBlock' to the low level format.
toByteStream :: HuffmanEncoding -> HeaderBlock -> ByteStream
toByteStream he hbs = BB.toByteString $ foldl' (<>) mempty $ map toBB hbs
  where
    toBB = fromHeaderField he

fromHeaderField :: HuffmanEncoding -> HeaderField -> Builder
fromHeaderField _  (Indexed idx)                = index idx
fromHeaderField he (Literal NotAdd (Idx idx) v) = indexedName he set01 idx v
fromHeaderField he (Literal NotAdd (Lit key) v) = newName     he set01 key v
fromHeaderField he (Literal Add    (Idx idx) v) = indexedName he set00 idx v
fromHeaderField he (Literal Add    (Lit key) v) = newName     he set00 key v

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

index :: Int -> Builder
index = BB.fromWord8 . set1 . I.encodeOne

-- Using Huffman encoding
indexedName :: HuffmanEncoding -> Setter -> Int -> HeaderValue -> Builder
indexedName he set idx v = pre <> vlen <> val
  where
    (p:ps) = I.encode 6 idx
    pre = BB.fromWord8s $ set p : ps
    value = S.encode he v
    valueLen = length value -- FIXME: performance
    vlen = BB.fromWord8s $ setH $ I.encode 7 valueLen
    val = BB.fromWord8s value

-- Using Huffman encoding
newName :: HuffmanEncoding -> Setter -> HeaderName -> HeaderValue -> Builder
newName he set k v = pre <> klen <> key <> vlen <> val
  where
    pre = BB.fromWord8 $ set 0
    key0 = S.encode he k
    keyLen = length key0 -- FIXME: performance
    value = S.encode he v
    valueLen = length value -- FIXME: performance
    klen = BB.fromWord8s $ setH $ I.encode 7 keyLen
    vlen = BB.fromWord8s $ setH $ I.encode 7 valueLen
    key = BB.fromWord8s key0
    val = BB.fromWord8s value

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

type Setter = Word8 -> Word8

-- Assuming MSBs are 0.
set1, set01, set00 :: Setter
set1  x = x `setBit` 7
set01 x = x `setBit` 6
set00   = id

setH :: [Word8] -> [Word8]
setH []     = error "setH"
setH (x:xs) = (x `setBit` 7) : xs