    -- |
    -- Module      : Foundation.Collection.Buildable
    -- License     : BSD-style
    -- Maintainer  : foundation
    -- Stability   : experimental
    -- Portability : portable
    --
    -- An interface for collections that can be built incrementally.
    --
    module Foundation.Collection.Buildable
        ( Buildable(..)
        , Builder(..)
        , BuildingState(..)
        , builderLift
        , build_
        ) where

    import           Basement.UArray
    import           Basement.UArray.Mutable
    import qualified Basement.BoxedArray as BA
    import qualified Basement.String as S
    import           Foundation.Collection.Element
    import           Basement.Compat.Base
    import           Basement.Monad
    import           Basement.MutableBuilder
    import           Basement.Compat.MonadTrans

    -- $setup
    -- >>> import Control.Monad.ST
    -- >>> import Basement.UArray
    -- >>> import Basement.Compat.Base
    -- >>> import Basement.OffsetSize

    -- | Collections that can be built chunk by chunk.
    --
    -- Use the 'Monad' instance of 'Builder' to chain 'append' operations
    -- and feed it into `build`:
    --
    -- >>> runST $ build 32 (append 'a' >> append 'b' >> append 'c') :: UArray Char
    -- "abc"
    class Buildable col where
        {-# MINIMAL append, build #-}

        -- | Mutable collection type used for incrementally writing chunks.
        type Mutable col :: * -> *

        -- | Unit of the smallest step possible in an `append` operation.
        --
        -- A UTF-8 character can have a size between 1 and 4 bytes, so this
        -- should be defined as 1 byte for collections of `Char`.
        type Step col

        append :: (PrimMonad prim) => Element col -> Builder col (Mutable col) (Step col) prim err ()

        build :: (PrimMonad prim)
              => Int -- ^ CountOf of a chunk
              -> Builder col (Mutable col) (Step col) prim err ()
              -> prim (Either err col)

    builderLift :: (Buildable c, PrimMonad prim)
                => prim a
                -> Builder c (Mutable c) (Step c) prim err a
    builderLift f = Builder $ State $ \(i, st, e) -> do
        ret <- f
        return (ret, (i, st, e))

    build_ :: (Buildable c, PrimMonad prim)
           => Int -- ^ CountOf of a chunk
           -> Builder c (Mutable c) (Step c) prim () ()
           -> prim c
    build_ sizeChunksI ab = either (\() -> internalError "impossible output") id <$> build sizeChunksI ab

    instance PrimType ty => Buildable (UArray ty) where
        type Mutable (UArray ty) = MUArray ty
        type Step (UArray ty) = ty
        append = builderAppend
        {-# INLINE append #-}
        build = builderBuild
        {-# INLINE build #-}

    instance Buildable (BA.Array ty) where
        type Mutable (BA.Array ty) = BA.MArray ty
        type Step (BA.Array ty) = ty

        append = BA.builderAppend
        {-# INLINE append #-}
        build = BA.builderBuild
        {-# INLINE build #-}

    instance Buildable S.String where
        type Mutable S.String = S.MutableString
        type Step S.String = Word8

        append = S.builderAppend
        {-# INLINE append #-}
        build = S.builderBuild
        {-# INLINE build #-}