{-
Helpers for the manipulation of rose trees.
Throughout, the _trunk_ refers to successive first children in the tree.
For example, in the tree 

>   Node a [ Node b [], Node c [Node d []] ]

the nodes in the trunk are [a,b] (note that d is not the first child of b).
 -}
module Dodge.Layout.Tree.Polymorphic
    where
import Dodge.RandomHelp

import Data.Tree
import Control.Monad.State
import System.Random

{- |
Creates a linear tree.
Safe.
-}
treeFromPost :: [a] -> a -> Tree a
treeFromPost []     y = Node y []
treeFromPost (x:xs) y = Node x [treeFromPost xs y]

{- |
Creates a tree with one trunk branch, 
input as a list, that ends in another tree.
-}
treeFromTrunk 
    :: [a]      -- ^ The trunk
    -> Tree a   -- ^ The end of the tree
    -> Tree a
treeFromTrunk [] t     = t
treeFromTrunk (x:xs) t = Node x [treeFromTrunk xs t]

{- |
Applies a function to the root of a tree.
-}
applyToRoot :: (a -> a) -> Tree a -> Tree a
applyToRoot f (Node t ts) = Node (f t) ts

-- | Consider defining this using generalised recursion patterns
treeSize :: Tree a -> Int
treeSize = length . flatten

{- |
Applies a function to a specific node determined by a list of indices.
Unsafe (partial function).
-}
applyToNode :: [Int] -> (a -> a) -> Tree a -> Tree a
applyToNode [] f t = applyToRoot f t
applyToNode (i:is) f (Node x xs) = Node x (ys ++ [applyToNode is f z] ++ zs)
  where
    (ys, z:zs) = splitAt i xs

{- |
Applies a function to the first node along a trunk that satisfies a given property.
-}
applyToSubTrunkBy :: (a -> Bool) -> (Tree a -> Tree a) -> Tree a -> Tree a
applyToSubTrunkBy cond f (Node x (t:ts))
    | cond x    = f (Node x (t:ts))
    | otherwise = Node x (applyToSubTrunkBy cond f t : ts)
applyToSubTrunkBy _ _ t = t

zipTree :: Tree a -> Tree b -> Tree (a,b)
zipTree (Node x xs) (Node y ys) = Node (x,y) $ zipWith zipTree xs ys

{- |
Makes each node into its child number, i.e. the index it has 
in the list of children of its parent.
-}
treeChildNums :: Tree a -> Tree Int
treeChildNums = setRoot 0
  where 
    setRoot :: Int -> Tree a -> Tree Int
    setRoot i (Node _ xs) = Node i (zipWith setRoot [0..] xs)

{- |
Makes each node into its path, i.e. the list of indices that, 
when followed from the root, lead to the node.
-}
treePaths :: Tree a -> Tree [a]
treePaths (Node x xs) = (x :) <$> Node [] (map treePaths xs)

{- |
Picks a random path in the tree.
Uniform probability that the path leads to any specific node.
-}
randomPath :: RandomGen g => Tree a -> State g [Int]
randomPath = takeOne . flatten . treePaths . treeChildNums

{- |
Apply a function to the value of a node; 
the node is picked uniformly at random.
-}
applyToRandomNode :: RandomGen g => (a -> a) -> Tree a -> State g (Tree a)
applyToRandomNode f t = do
    p <- randomPath t
    return $ applyToNode p f t

{- |
Add a forest to the end of a tree (along the trunk).
-}
addToTrunk :: Tree a -> [Tree a] -> Tree a
addToTrunk (Node x []) f = Node x f
addToTrunk (Node x (t:ts)) f = Node x (addToTrunk t f : ts)

{- |
Find the depth of a tree along the trunk.
-}
trunkDepth :: Tree a -> Int
trunkDepth (Node _ []) = 0
trunkDepth (Node _ (x:_)) = trunkDepth x + 1

{- |
Split a tree at a given point along its trunk.
-}
splitTrunkAt 
    :: Int      -- ^ Split depth
    -> Tree a -> (Tree a, [Tree a])
splitTrunkAt 0 (Node x xs) = (Node x [],xs)
splitTrunkAt i (Node y (x:xs)) = 
    let (t, ts) = splitTrunkAt (i-1) x
    in (Node y (t : xs) , ts)
splitTrunkAt _ (Node _ []) = error "Trying to split to short a trunk"

{- |
Split a tree at a random point along its trunk.
-}
splitTrunk :: RandomGen g => Tree a -> State g (Tree a, [Tree a])
splitTrunk t = do
    i <- state $ randomR (0, trunkDepth t)
    return $ splitTrunkAt i t
