-- | Common functionality for rendering pretty output.
module DataFlow.PrettyRenderer where

import Control.Monad.State
import Control.Monad.Writer

type Indent = Int
type IndentNext = Bool
data RendererState = RendererState Indent IndentNext

-- | The Renderer represents some output generator that runs on a 'Diagram'.
type Renderer t = WriterT [String] (State RendererState) t

-- | Write a string to the output (no linefeed).
write :: String -> Renderer ()
write s = do
  (RendererState n indentNext) <- lift get
  if indentNext
    then tell [replicate n ' ' ++ s]
    else tell [s]
  put $ RendererState n False

-- | Write a string to the output (with linefeed).
writeln :: String -> Renderer ()
writeln s = do
  write s
  write "\n"
  modify $ \(RendererState n _) -> RendererState n True

-- | Increase indent with 2 spaces.
indent :: Renderer ()
indent = modify $ \(RendererState n indentNext) -> RendererState (n + 2) indentNext

-- | Decrease indent with 2 spaces.
dedent :: Renderer ()
dedent = modify $ \(RendererState n indentNext) -> RendererState (n - 2) indentNext

-- | Indent the output of gen with 2 spaces.
withIndent :: Renderer () -> Renderer ()
withIndent gen = do
  indent
  gen
  dedent

renderWithIndent :: Renderer () -> String
renderWithIndent r =
  concat $ evalState (execWriterT r) (RendererState 0 False)
