    -- | Pretty printing of graphs.

    module GraphPpr (
            dumpGraph,
            dotGraph
    )
    where

    import GraphBase

    import Outputable
    import Unique
    import UniqSet
    import UniqFM

    import Data.List
    import Data.Maybe

    -- | Pretty print a graph in a somewhat human readable format.
    dumpGraph
            :: (Outputable k, Outputable cls, Outputable color)
            => Graph k cls color -> SDoc

    dumpGraph graph
            =  text "Graph"
            $$ (vcat $ map dumpNode $ eltsUFM $ graphMap graph)

    dumpNode
            :: (Outputable k, Outputable cls, Outputable color)
            => Node k cls color -> SDoc

    dumpNode node
            =  text "Node " <> ppr (nodeId node)
            $$ text "conflicts "
                    <> parens (int (sizeUniqSet $ nodeConflicts node))
                    <> text " = "
                    <> ppr (nodeConflicts node)

            $$ text "exclusions "
                    <> parens (int (sizeUniqSet $ nodeExclusions node))
                    <> text " = "
                    <> ppr (nodeExclusions node)

            $$ text "coalesce "
                    <> parens (int (sizeUniqSet $ nodeCoalesce node))
                    <> text " = "
                    <> ppr (nodeCoalesce node)

            $$ space

    -- | Pretty print a graph in graphviz .dot format.
    --      Conflicts get solid edges.
    --      Coalescences get dashed edges.
    dotGraph
            :: ( Uniquable k
               , Outputable k, Outputable cls, Outputable color)
            => (color -> SDoc)  -- ^ What graphviz color to use for each node color
                                --  It's usually safe to return X11 style colors here,
                                --  ie "red", "green" etc or a hex triplet #aaff55 etc
            -> Triv k cls color
            -> Graph k cls color -> SDoc

    dotGraph colorMap triv graph
     = let  nodes   = eltsUFM $ graphMap graph
       in   vcat
                    (  [ text "graph G {" ]
                    ++ map (dotNode colorMap triv) nodes
                    ++ (catMaybes $ snd $ mapAccumL dotNodeEdges emptyUniqSet nodes)
                    ++ [ text "}"
                       , space ])

    dotNode :: ( Uniquable k
               , Outputable k, Outputable cls, Outputable color)
            => (color -> SDoc)
            -> Triv k cls color
            -> Node k cls color -> SDoc

    dotNode colorMap triv node
     = let  name    = ppr $ nodeId node
            cls     = ppr $ nodeClass node

            excludes
                    = hcat $ punctuate space
                    $ map (\n -> text "-" <> ppr n)
                    $ uniqSetToList $ nodeExclusions node

            preferences
                    = hcat $ punctuate space
                    $ map (\n -> text "+" <> ppr n)
                    $ nodePreference node

            expref  = if and [isEmptyUniqSet (nodeExclusions node), null (nodePreference node)]
                            then empty
                            else text "\\n" <> (excludes <+> preferences)

            -- if the node has been colored then show that,
            --      otherwise indicate whether it looks trivially colorable.
            color
                    | Just c        <- nodeColor node
                    = text "\\n(" <> ppr c <> text ")"

                    | triv (nodeClass node) (nodeConflicts node) (nodeExclusions node)
                    = text "\\n(" <> text "triv" <> text ")"

                    | otherwise
                    = text "\\n(" <> text "spill?" <> text ")"

            label   =  name <> text " :: " <> cls
                    <> expref
                    <> color

            pcolorC = case nodeColor node of
                            Nothing -> text "style=filled fillcolor=white"
                            Just c  -> text "style=filled fillcolor=" <> doubleQuotes (colorMap c)

            pout    = text "node [label=" <> doubleQuotes label <> space <> pcolorC <> text "]"
                    <> space <> doubleQuotes name
                    <> text ";"

     in     pout

    -- | Nodes in the graph are doubly linked, but we only want one edge for each
    --      conflict if the graphviz graph. Traverse over the graph, but make sure
    --      to only print the edges for each node once.

    dotNodeEdges
            :: ( Uniquable k
               , Outputable k, Outputable cls, Outputable color)
            => UniqSet k
            -> Node k cls color
            -> (UniqSet k, Maybe SDoc)

    dotNodeEdges visited node
            | elementOfUniqSet (nodeId node) visited
            = ( visited
              , Nothing)

            | otherwise
            = let   dconflicts
                            = map (dotEdgeConflict (nodeId node))
                            $ uniqSetToList
                            $ minusUniqSet (nodeConflicts node) visited

                    dcoalesces
                            = map (dotEdgeCoalesce (nodeId node))
                            $ uniqSetToList
                            $ minusUniqSet (nodeCoalesce node) visited

                    out     =  vcat dconflicts
                            $$ vcat dcoalesces

              in    ( addOneToUniqSet visited (nodeId node)
                    , Just out)

            where   dotEdgeConflict u1 u2
                            = doubleQuotes (ppr u1) <> text " -- " <> doubleQuotes (ppr u2)
                            <> text ";"

                    dotEdgeCoalesce u1 u2
                            = doubleQuotes (ppr u1) <> text " -- " <> doubleQuotes (ppr u2)
                            <> space <> text "[ style = dashed ];"