-- | The Unparse type class and its 'ParseTree' instance, as well as some text manipulation.
module Language.GroteTrap.Unparse (

  -- * Class Unparse
  Unparse(..),
  
  -- * Text utility functions
  merge, over

  ) where

import Language.GroteTrap.Range
import Language.GroteTrap.ParseTree


------------------------------------
-- Class Unparse
------------------------------------


-- | Types that are unparsable. Unparsing is like prettyprinting, except that instead of pretty source the original source code is retrieved. This means unparsing is only possible for values that were the result of an earlier parse.
class Unparse p where
  unparse :: p -> String


------------------------------------
-- instance Unparse ParseTree
------------------------------------


instance Unparse ParseTree where
  unparse = foldParseTree $ ParseTreeAlg
    ( \pos name -> indent pos name )
    ( \pos value -> indent pos $ show value )
    unparseUnary
    unparseBinary
    unparseNary
    unparseCall
    unparseParens


indent :: Pos -> String -> String
indent n s = replicate n ' ' ++ s


unparseUnary :: Range -> String -> String -> String
unparseUnary (begin, _) op sub = indent begin op `over` sub


unparseBinary :: Range -> String -> String -> String -> String
unparseBinary (begin, _) op left right = left `over` indent begin op `over` right


unparseNary :: Bool -> [Range] -> String -> [String] -> String
unparseNary _ ranges op children = foldl over "" children `over` foldl over "" (map place ranges)
  where place (begin, _) = indent begin op


unparseParens :: Range -> String -> String
unparseParens (begin, end) sub = indent begin "(" `over` sub `over` indent (end - 1) ")"


unparseCall = error "Whoops! Not implemented"


------------------------------------
-- Text utility functions
------------------------------------


-- | @over over' under@ places @over'@ over @under@. The resulting string has the same characters as @over'@ does, except where @over'@ contains spaces; at those positions, the character from @under@ shows. If @under@ is longer than @over'@, @over'@ is padded with enough spaces to show all rest of @under@.
over :: String -> String -> String
over over' under = take n $ zipWith f (pad over') (pad under)
  where f ' ' b = b
        f  a  _ = a
        pad str = str ++ repeat ' '
        n = length over' `max` length under

-- | Merge folds many strings 'over' each other.
merge :: [String] -> String
merge = foldr over ""