    {-# LANGUAGE RecordWildCards, PatternGuards #-}

    module Hoogle.DataBase.Items(Items, createItems, entriesItems) where

    import General.Base
    import General.Util
    import General.Web
    import Hoogle.Type.All
    import qualified Data.Map as Map
    import Hoogle.Store.All

    -- Invariant: items are by order of EntryScore
    newtype Items = Items {fromItems :: Defer [Once Entry]}

    instance NFData Items where
        rnf (Items a) = rnf a

    entriesItems :: Items -> [Once Entry]
    entriesItems = fromDefer . fromItems

    instance Store Items where
        put (Items a) = put1 a
        get = get1 Items

    instance Show Items where
        show (Items x) = "== Entries ==\n\n" ++ show x

    instance Monoid Items where
        mempty = mergeItems []
        mappend x y = mergeItems [x,y]
        mconcat = mergeItems

    createItems :: [TextItem] -> Items
    createItems xs = mergeItems [Items $ Defer $ fs Nothing Nothing xs]
        where
            fs pkg mod [] = []
            fs pkg mod (x:xs) = r : fs pkg2 mod2 xs
                where r = f pkg2 mod2 x
                      pkg2 = if itemLevel x == 0 then Just r else pkg
                      mod2 = if itemLevel x == 1 then Just r else mod

            f pkg mod TextItem{..} = once $ Entry [(url, catMaybes [pkg,mod])] itemKind itemLevel itemName itemDisp
                (readDocsHTML itemDocs) itemPriority itemKey itemType
                where url | Just pkg <- pkg, itemLevel == 1 || (itemLevel > 1 && isNothing mod) = entryURL (fromOnce pkg) `combineURL` itemURL
                          | Just mod <- mod, itemLevel > 1 = entryURL (fromOnce mod) `combineURL` itemURL
                          | otherwise = itemURL

    -- | Given a set of items, which may or may not individually satisfy the entryScore invariant,
    --   make it so they _do_ satisfy the invariant.
    --   Also merge any pair of items which are similar enough.
    --
    --   If something which is a parent gets merged, then it will still point into the database,
    --   but it won't be very useful.
    mergeItems :: [Items] -> Items
    mergeItems = Items . Defer . sortOn (entryScore . fromOnce) . Map.elems . foldl' add Map.empty . concatMap entriesItems
        where
            add mp x = Map.insertWith (\x1 x2 -> once $ entryJoin (fromOnce x1) (fromOnce x2))
                                      (entryUnique $ fromOnce x) x mp