#ifdef USE_MONO_PAT_BINDS
#endif
module Blaze.ByteString.Builder.HTTP (
  
    chunkedTransferEncoding
  , chunkedTransferTerminator
  ) where
import Data.Monoid
import qualified Data.ByteString       as S
import Data.ByteString.Char8 ()
import Foreign
import Blaze.ByteString.Builder.Internal
import Blaze.ByteString.Builder.Internal.Types
import Blaze.ByteString.Builder.Internal.UncheckedShifts
import Blaze.ByteString.Builder.ByteString (copyByteString)
import qualified Blaze.ByteString.Builder.Char8 as Char8
writeCRLF :: Write
writeCRLF = Char8.writeChar '\r' `mappend` Char8.writeChar '\n'
execWrite :: Write -> Ptr Word8 -> IO ()
execWrite w op = do
    _ <- runPoke (getPoke w) op
    return ()
pokeWord32HexN :: Int -> Word32 -> Ptr Word8 -> IO ()
pokeWord32HexN n0 w0 op0 =
    go w0 (op0 `plusPtr` (n0  1))
  where
    go !w !op
      | op < op0  = return ()
      | otherwise = do
          let nibble :: Word8
              nibble = fromIntegral w .&. 0xF
              hex | nibble < 10 = 48 + nibble
                  | otherwise   = 55 + nibble
          poke op hex
          go (w `shiftr_w32` 4) (op `plusPtr` (1))
iterationsUntilZero :: Integral a => (a -> a) -> a -> Int
iterationsUntilZero f = go 0
  where
    go !count 0  = count
    go !count !x = go (count+1) (f x)
word32HexLength :: Word32 -> Int
word32HexLength = max 1 . iterationsUntilZero (`shiftr_w32` 4)
writeWord32Hex :: Word32 -> Write
writeWord32Hex w =
    boundedWrite (2 * sizeOf w) (pokeN len $ pokeWord32HexN len w)
  where
    len = word32HexLength w
chunkedTransferEncoding :: Builder -> Builder
chunkedTransferEncoding (Builder b) =
    fromBuildStepCont transferEncodingStep
  where
    finalStep !(BufRange op _) = return $ Done op ()
    transferEncodingStep k = go (b (buildStep finalStep))
      where
        go innerStep !(BufRange op ope)
          
          | outRemaining < minimalBufferSize =
              return $ bufferFull minimalBufferSize op (go innerStep)
          | otherwise = do
              let !brInner@(BufRange opInner _) = BufRange
                     (op  `plusPtr` (chunkSizeLength + 2))     
                     (ope `plusPtr` (maxAfterBufferOverhead)) 
                  
                  
                  
                  wrapChunk :: Ptr Word8 -> (Ptr Word8 -> IO (BuildSignal a))
                            -> IO (BuildSignal a)
                  wrapChunk !opInner' mkSignal
                    | opInner' == opInner = mkSignal op
                    | otherwise           = do
                        pokeWord32HexN chunkSizeLength
                            (fromIntegral $ opInner' `minusPtr` opInner)
                            op
                        execWrite writeCRLF (opInner `plusPtr` (2))
                        execWrite writeCRLF opInner'
                        mkSignal (opInner' `plusPtr` 2)
              
              signal <- runBuildStep innerStep brInner
              case signal of
                Done opInner' _ ->
                    wrapChunk opInner' $ \op' -> do
                      let !br' = BufRange op' ope
                      k br'
                BufferFull minRequiredSize opInner' nextInnerStep ->
                    wrapChunk opInner' $ \op' ->
                      return $! bufferFull
                        (minRequiredSize + maxEncodingOverhead)
                        op'
                        (go nextInnerStep)
                InsertByteString opInner' bs nextInnerStep
                  | S.null bs ->                        
                      wrapChunk opInner' $ \op' ->
                        return $! insertByteString
                          op' S.empty
                          (go nextInnerStep)
                  | otherwise ->                        
                      wrapChunk opInner' $ \op' -> do
                        
                        
                        !op'' <- (`runPoke` op') $ getPoke $
                            writeWord32Hex (fromIntegral $ S.length bs)
                            `mappend` writeCRLF
                        
                        return $! InsertByteString
                          op'' bs
                          (unBuilder (fromWrite writeCRLF) $
                            buildStep $ go nextInnerStep)
          where
            
            
            
            
            minimalChunkSize  = 1
            
            maxBeforeBufferOverhead = sizeOf (undefined :: Int) + 2 
            maxAfterBufferOverhead  = 2 +                           
                                      sizeOf (undefined :: Int) + 2 
            maxEncodingOverhead = maxBeforeBufferOverhead + maxAfterBufferOverhead
            minimalBufferSize = minimalChunkSize + maxEncodingOverhead
            
            outRemaining :: Int
            outRemaining    = ope `minusPtr` op
            chunkSizeLength = word32HexLength $ fromIntegral outRemaining
chunkedTransferTerminator :: Builder
chunkedTransferTerminator = copyByteString "0\r\n\r\n"