    {-# LANGUAGE TupleSections, RecordWildCards, ScopedTypeVariables, PatternGuards, DeriveDataTypeable, GADTs #-}

    module Output.Items(writeItems, lookupItem, listItems) where

    import Control.Monad
    import Data.List.Extra
    import qualified Data.ByteString as BS
    import qualified Data.ByteString.Lazy.Char8 as LBS
    import qualified Data.ByteString.Lazy.UTF8 as UTF8
    import qualified Codec.Compression.GZip as GZip
    import General.Str

    import Input.Item
    import General.Util
    import General.Store
    import General.Conduit

    data Items a where Items :: Items BS.ByteString deriving Typeable

    outputItem :: Target -> [String]
    outputItem Target{..} =
        [if null targetURL then "." else targetURL
        ,maybe "." (joinPair " ") targetPackage
        ,maybe "." (joinPair " ") targetModule
        ,if null targetType then "." else targetType
        ,targetItem] ++
        replace [""] ["."] (lines targetDocs)

    inputItem :: [String] -> Target
    inputItem (url:pkg:modu:typ:self:docs) = targetExpandURL $
        Target (if url == "." then "" else url) (f pkg) (f modu) (if typ == "." then "" else typ) self (unlines $ replace ["."] [""] docs)
        where
            f "." = Nothing
            f x = Just (word1 x)

    -- write all the URLs, docs and enough info to pretty print it to a result
    -- and replace each with an identifier (index in the space) - big reduction in memory
    writeItems :: StoreWrite -> (ConduitM (Maybe Target, item) (Maybe TargetId, item) IO () -> IO a) -> IO a
    writeItems store act = act $ do
        void $ (\f -> mapAccumMC f 0) $ \pos (target, item) -> case target of
            Nothing -> return (pos, (Nothing, item))
            Just target -> do
                let bs = LBS.toStrict $ GZip.compress $ lstrPack $ unlines $ outputItem target
                liftIO $ do
                    storeWritePart store Items $ intToBS $ BS.length bs
                    storeWritePart store Items bs
                let pos2 = pos + fromIntegral (intSize + BS.length bs)
                return (pos2, (Just $ TargetId pos, item))

    listItems :: StoreRead -> [Target]
    listItems store = unfoldr f $ storeRead store Items
        where
            f x | BS.null x = Nothing
                | (n,x) <- BS.splitAt intSize x
                , n <- intFromBS n
                , (this,x) <- BS.splitAt n x
                = Just (inputItem $ lines $ UTF8.toString $ GZip.decompress $ LBS.fromChunks [this], x)

    lookupItem :: StoreRead -> (TargetId -> Target)
    lookupItem store =
        let x = storeRead store Items
        in \(TargetId i) ->
            let i2 = fromIntegral i
                n = intFromBS $ BS.take intSize $ BS.drop i2 x
            in inputItem $ lines $ UTF8.toString $ GZip.decompress $ LBS.fromChunks $ return $ BS.take n $ BS.drop (i2 + intSize) x