module Problem2c where

import Data.List

data Doc = Doc :$$ Doc
         | Doc :<> Doc
         | Empty
         | Text String
  deriving Show

($$) :: Doc -> Doc -> Doc
($$) = (:$$)

empty :: Doc
empty = Empty

(<>) :: Doc -> Doc -> Doc
(<>) = (:<>)

text :: String -> Doc
text = Text

-- Derived combinators
indent :: Int -> Doc -> Doc
indent n x = text (replicate n ' ') <> x

(<+>) :: Doc -> Doc -> Doc
x <+> y = x <> indent 1 y

-- Run function
render :: Doc -> String
render = unlines . toLines

toLines :: Doc -> [String]
toLines (x :$$ y) = toLines x ++ toLines y
toLines Empty     = []
toLines (Text s)  = [s]
toLines (x :<> y) = toLines x `hcat` toLines y
  where
    -- Same as the shallow implementation of (<>)
    hcat []       ys       = ys
    hcat xs       []       = xs
    hcat [x]      (y : ys) = (x ++ y) : map (replicate (length x) ' ' ++) ys
    hcat (x : xs) ys       = x : hcat xs ys