{-|
    Search for a type signature and context through a graph.

    Return results in best-first order, taking account of which
    nodes and edges have already been paid for.
-}

module Hoogle.DataBase.TypeSearch.Graph(
    Graph, newGraph,
    graphSearch
    ) where

import Hoogle.DataBase.TypeSearch.TypeScore
import Hoogle.DataBase.TypeSearch.Binding
import Hoogle.DataBase.TypeSearch.Result
import Hoogle.DataBase.Aliases
import Hoogle.Item.All
import Hoogle.TypeSig.All
import Data.Generics.Uniplate
import Data.Binary.Defer
import Data.Binary.Defer.Index
import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import qualified Data.Heap as Heap
import General.Code
import Data.Key



newtype Graph = Graph (Map.Map Type [Node])

-- the Type's are stored in reverse, to make box/unbox computations quicker
data Node = Node [Type] [(Link EntryInfo,ArgPos)]


instance Show Graph where
    show (Graph mp) = unlines $ concatMap f $ Map.toList mp
        where f (t,ns) = show (transform g t) : map (("  "++) . show) ns
              g x = if x == TVar "" then TVar "_" else x

instance Show Node where
    show (Node t xs) = unwords $ map show t ++ "=" : [show (linkKey a) ++ "." ++ show b | (a,b) <- xs]


instance BinaryDefer Graph where
    put (Graph a) = put1 a
    get = get1 Graph

instance BinaryDefer Node where
    put (Node a b) = put2 a b
    get = get2 Node


---------------------------------------------------------------------
-- GRAPH CONSTRUCTION


typeStructure :: Type -> Type
typeStructure = transform f
    where f x = if isTLit x || isTVar x then TVar "" else x

typeUnstructure :: Type -> [Type]
typeUnstructure = reverse . filter (\x -> isTLit x || isTVar x) . universe


newGraph :: [(Link EntryInfo, ArgPos, Type)] -> Graph
newGraph = Graph . Map.map newNode . foldl' f Map.empty 
    where f mp x = Map.insertWith (++) (typeStructure $ thd3 x) [x] mp


newNode :: [(Link EntryInfo, ArgPos, Type)] -> [Node]
newNode = map (uncurry Node) . sortGroupFsts . map (\(a,b,c) -> (typeUnstructure c,(a,b)))




---------------------------------------------------------------------
-- GRAPH SEARCHING

-- must search for each (node,bindings) pair, rather than just nodes

graphSearch :: Graph -> Type -> [ResultArg]
graphSearch (Graph mp) t = [ResultArg e p b | (b,ep) <- sortFst xs, (e,p) <- ep]
    where
        xs = f newBinding s ++ f newBindingRebox (TApp (TVar "") [s]) ++
             concat [f newBindingUnbox x | TApp (TVar "") [x] <- [s]]
        u = typeUnstructure t
        s = typeStructure t

        f bind x = mapMaybe (graphCheck bind u) $ Map.findWithDefault [] x mp


graphCheck :: Binding -> [Type] -> Node -> Maybe (Binding, [(Link EntryInfo,ArgPos)])
graphCheck b xs (Node ys res) = do
    b <- f b (zip xs ys)
    return (b, res)
    where
        f b [] = Just b
        f b (x:xs) = do
            b <- addBinding x b
            f b xs