{-| Combining and composing trees of trees.  -}
module Dodge.Tree.Compose
    ( expandTree
    , singleUseAll
    , singleUseNone
    , passUntilUseAll
    , chainUses
    , useAllAtEnd
    , changeToPassDown
    , overwriteLabel
    , applyToCompRoot
    ) where
import Dodge.Tree.Compose.Data
import Dodge.Tree.Polymorphic
import Dodge.Base
import Data.Tree
import Control.Lens

expandTree :: CompTree a -> Tree a
expandTree (Node root extChildren) = case root of
    Node (UseAll x)     _   -> Node x (map expandTree extChildren)
    Node (UseSome is x) _   -> Node x (map (expandTree . \i -> extChildren !! i) is)
    Node (UseNone _) _   -> fmap _unCompose root
    Node (UseLabel _ _) _   -> fmap _unCompose root
    Node (PassDown x)   xs  -> Node x $ map (expandTree . (`Node` extChildren)) xs
    Node (SplitDown x)  xs  -> Node x $ map expandTree $ zipWith Node xs 
            $ map (:[]) extChildren ++ repeat []

applyToCompRoot :: (a -> a) -> SubCompTree a -> SubCompTree a
applyToCompRoot f = applyToRoot (unCompose %~ f)

overwriteLabel :: Int -> (a -> ComposingNode a) -> SubCompTree a -> [SubCompTree a] -> SubCompTree a
overwriteLabel i f t ts = errorHead ("tried to overwriteLabel " ++ show i)
    $ updateSingleNode islabel update t
  where
    islabel (UseLabel j _) | i == j = True
    islabel _ = False
    update (Node cr _) = Node (f $ _unCompose cr) ts

passUntilUseAll :: SubCompTree a -> [SubCompTree a] -> SubCompTree a
passUntilUseAll (Node (UseAll x) _) ts' = Node (PassDown x) ts'
passUntilUseAll (Node cn ts) ts' = Node (PassDown (_unCompose cn)) $ map (`passUntilUseAll` ts') ts

chainUses :: [SubCompTree a] -> SubCompTree a
chainUses (t:s:ts) = chainUses (passUntilUseAll t [s]:ts)
chainUses [t] = t
chainUses []  = error "tried to concatenate an empty list of SubCompTrees"

singleUseAll :: a -> Tree (ComposingNode a)
singleUseAll x = Node (UseAll x) []

useAllAtEnd :: Tree a -> SubCompTree a
useAllAtEnd tree = case tree of
    Node x (t:ts)   -> Node (PassDown x) (useAllAtEnd t : map (fmap UseNone) ts)
    Node x []       -> Node (UseAll x) []

singleUseNone :: a -> SubCompTree a
singleUseNone x = Node (UseNone x) []

changeToPassDown :: ComposingNode a -> ComposingNode a
changeToPassDown x = PassDown (_unCompose x)
