module Strelka.ResponseBodyBuilding.Builder where

import Strelka.Prelude
import qualified Data.ByteString as C
import qualified Data.ByteString.Lazy as D
import qualified Data.ByteString.Builder as E
import qualified Data.Text.Encoding as H
import qualified Data.Text.Lazy as F
import qualified Data.Text.Lazy.Encoding as I
import qualified Data.Text.Lazy.Builder as J


{-|
A builder of the response body.
-}
newtype Builder =
  Builder ((ByteString -> IO ()) -> IO () -> IO ())

instance IsString Builder where
  fromString string =
    lazyBytesBuilder (E.stringUtf8 string)

instance Monoid Builder where
  mempty =
    Builder (\_ flush -> flush)
  mappend (Builder cont1) (Builder cont2) =
    Builder (\feed flush -> cont1 feed (pure ()) *> cont2 feed flush)

instance Semigroup Builder where
  (<>) = mappend

{-|
Lift ByteString.
-}
bytes :: ByteString -> Builder
bytes x =
  Builder (\feed flush -> feed x *> flush)

{-|
Lift lazy ByteString.
-}
lazyBytes :: D.ByteString -> Builder
lazyBytes x =
  Builder (\feed flush -> D.foldlChunks (\io chunk -> io >> feed chunk) (pure ()) x >> flush)

{-|
Lift a ByteString builder.
-}
lazyBytesBuilder :: E.Builder -> Builder
lazyBytesBuilder =
  lazyBytes . E.toLazyByteString

{-|
Lift Text.
-}
text :: Text -> Builder
text text =
  bytes (H.encodeUtf8 text)

{-|
Lift lazy Text.
-}
lazyText :: F.Text -> Builder
lazyText text =
  Builder impl
  where
    impl feed flush =
      F.foldlChunks step (pure ()) text *> flush
      where
        step io textChunk =
          io *> feed bytesChunk
          where
            bytesChunk =
              H.encodeUtf8 textChunk

{-|
Lift a Text builder.
-}
lazyTextBuilder :: J.Builder -> Builder
lazyTextBuilder =
  lazyText . J.toLazyText

{-|
Lift a handler of the feeding and flushing actions.
-}
actions :: ((ByteString -> IO ()) -> IO () -> IO ()) -> Builder
actions =
  Builder