{-# LANGUAGE CPP #-}
module Language.Dot.Pretty
  ( prettyPrintDot
  , renderDot
  , PP(..)
  )
  where

#if MIN_VERSION_base(4,11,0)
import Prelude hiding ((<>))
#endif

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

import Numeric
import Text.PrettyPrint

import Language.Dot.Syntax

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

prettyPrintDot :: Graph -> Doc
prettyPrintDot = pp

renderDot :: Graph -> String
renderDot = render . pp

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

class PP a where
  pp :: a -> Doc

instance (PP a) => PP (Maybe a) where
  pp (Just v) = pp v
  pp Nothing  = empty

instance PP Graph where
  pp (Graph s d mi ss) = pp s <+> pp d <+> pp mi <+> lbrace $+$ indent (vcat' ss) $+$ rbrace

instance PP GraphStrictness where
  pp StrictGraph   = text "strict"
  pp UnstrictGraph = empty

instance PP GraphDirectedness where
  pp DirectedGraph   = text "digraph"
  pp UndirectedGraph = text "graph"

instance PP Id where
  pp (NameId v)    = text v
  pp (StringId v)  = doubleQuotes (text v)
  pp (IntegerId v) = integer v
  pp (FloatId v)   = ffloat v
  pp (XmlId v)     = langle <> pp v <> rangle

instance PP Statement where
  pp (NodeStatement ni as)       = pp ni <+> if not (null as) then brackets (hsep' as) else empty
  pp (EdgeStatement es as)       = hsep' es <+> if not (null as) then brackets (hsep' as) else empty
  pp (AttributeStatement t as)   = pp t <+> brackets (hsep' as)
  pp (AssignmentStatement i0 i1) = pp i0 <> equals <> pp i1
  pp (SubgraphStatement s)       = pp s

instance PP AttributeStatementType where
  pp GraphAttributeStatement = text "graph"
  pp NodeAttributeStatement  = text "node"
  pp EdgeAttributeStatement  = text "edge"

instance PP Attribute where
  pp (AttributeSetTrue i)      = pp i
  pp (AttributeSetValue i0 i1) = pp i0 <> equals <> pp i1

instance PP NodeId where
  pp (NodeId i mp) = pp i <> pp mp

instance PP Port where
  pp (PortI i mc) = colon <> pp i <> maybe empty ((colon <>) . pp) mc
  pp (PortC c)    = colon <> pp c

instance PP Compass where
  pp CompassN  = text "n"
  pp CompassE  = text "e"
  pp CompassS  = text "s"
  pp CompassW  = text "w"
  pp CompassNE = text "ne"
  pp CompassNW = text "nw"
  pp CompassSE = text "se"
  pp CompassSW = text "sw"

instance PP Subgraph where
  pp (NewSubgraph mi ss) = text "subgraph" <+> pp mi <+> lbrace $+$ indent (vcat' ss) $+$ rbrace
  pp (SubgraphRef i)     = text "subgraph" <+> pp i

instance PP Entity where
  pp (ENodeId et ni)   = pp et <+> pp ni
  pp (ESubgraph et sg) = pp et <+> pp sg

instance PP EdgeType where
  pp NoEdge         = empty
  pp DirectedEdge   = text "->"
  pp UndirectedEdge = text "--"

instance PP Xml where
  pp (XmlEmptyTag n as) = langle <> pp n <+> hsep' as <> slash <> rangle
  pp (XmlTag n as xs)   = langle <> pp n <+> hsep' as <> rangle <> hcat' xs <> langle <> slash <> pp n <> rangle
  pp (XmlText t)        = text t

instance PP XmlName where
  pp (XmlName n) = text n

instance PP XmlAttribute where
  pp (XmlAttribute n v) = pp n <> equals <> pp v

instance PP XmlAttributeValue where
  pp (XmlAttributeValue v) = doubleQuotes (text v)

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

indent :: Doc -> Doc
indent = nest 2

hcat' :: (PP a) => [a] -> Doc
hcat' = hcat . map pp

hsep' :: (PP a) => [a] -> Doc
hsep' = hsep . map pp

vcat' :: (PP a) => [a] -> Doc
vcat' = vcat . map pp

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

langle :: Doc
rangle :: Doc
slash  :: Doc

langle = char '<'
rangle = char '>'
slash  = char '/'

ffloat :: Float -> Doc
ffloat v = text (showFFloat Nothing v "")