{-|
Combining and composing trees with 'Either' nodes.
-}
module Dodge.Layout.Tree.Either
    ( expandTreeBy
    , appendEitherTree
    , connectRoom
    , connectTrunk
    , deadRoom
    ) where
import Data.Tree

-- | 'Left' elements get new children as given by the node function, 
-- 'Right' elements inherit the children from the base tree
expandTreeBy 
    :: (a -> Tree (Either b b)) -- ^ Node function
    -> Tree a                   -- ^ Base tree
    -> Tree b
expandTreeBy f (Node x []) = fmap removeEither (f x)
expandTreeBy f (Node x xs) = appendAndRemove $ f x
  where
    appendAndRemove (Node (Left y)  ys) = Node y (map appendAndRemove ys)
    appendAndRemove (Node (Right y) _ ) = Node y (map (expandTreeBy f) xs)

-- | Appends a second either forest at the 'Right' elements of a first either
-- tree.
-- Makes such 'Right' elements into 'Left's.
appendEitherTree 
    :: Tree (Either a a)    -- ^ The first tree
    -> [Tree (Either a a)]    -- ^ The forest to append
    -> Tree (Either a a)
appendEitherTree (Node (Left x) ts) ts'  = Node (Left x) $ map (`appendEitherTree` ts') ts
appendEitherTree (Node (Right x) _) ts' = Node (Left x) ts'

removeEither :: Either p p -> p
removeEither (Left y)  = y
removeEither (Right y) = y

{-  |
Make a singleton connection of an Either Tree.
-}
connectRoom :: a -> Tree (Either a a)
connectRoom r = Node (Right r) [] 

{-  |
Make a single Either connection at the end of a tree.
-}
connectTrunk :: Tree a -> Tree (Either a a)
connectTrunk (Node x (t:ts)) = Node (Left x) (connectTrunk t : map (fmap Left) ts)
connectTrunk (Node x []) = Node (Right x) []

{- |
Make a singleton dead end of an Either Tree.
-}
deadRoom :: a -> Tree (Either a a)
deadRoom r = Node (Left r) []
