module Data.BinaryTree.Zipper where

import Data.BinaryTree
import Data.Semigroup

--------------------------------------------------------------------------------

data Ctx a = Top | L (Ctx a) a (BinaryTree a) | R (BinaryTree a) a (Ctx a)
           deriving (Show,Read,Eq,Ord,Functor,Foldable,Traversable)

data BinaryTreeZipper a = Loc (BinaryTree a) (Ctx a)
           deriving (Show,Read,Eq,Ord,Functor,Foldable,Traversable)

-- | Focus on the root
top   :: BinaryTree a -> BinaryTreeZipper a
top t = Loc t Top

-- | Go to the left child
left                            :: BinaryTreeZipper a -> Maybe (BinaryTreeZipper a)
left (Loc (Internal l x r) ctx) = Just $ Loc l (L ctx x r)
left (Loc Nil _)                = Nothing

-- | Go to the right child
right                            :: BinaryTreeZipper a -> Maybe (BinaryTreeZipper a)
right (Loc (Internal l x r) ctx) = Just $ Loc r (R l x ctx)
right (Loc Nil _)                = Nothing

-- | Move to the parent
up                     :: BinaryTreeZipper a -> Maybe (BinaryTreeZipper a)
up (Loc _ Top)         = Nothing
up (Loc l (L ctx x r)) = Just $ Loc (Internal l x r) ctx
up (Loc r (R l x ctx)) = Just $ Loc (Internal l x r) ctx

-- | Navigate to the root
toRoot   :: BinaryTreeZipper a -> BinaryTreeZipper a
toRoot z = toRoot' z (Just z)
  where
    toRoot' z' Nothing   = z'
    toRoot' _  (Just z') = toRoot' z' (up z')


-- | Returns a list of zippers; one focussed on each node in the tree
visitAll   :: BinaryTree a -> [BinaryTreeZipper a]
visitAll t = visitAll' (top t)
  where
    f           = maybe [] visitAll'
    visitAll' z = z : f (left z) <> f (right z)

-- | Get the value stored at the current node
accessZ           :: BinaryTreeZipper a -> Maybe a
accessZ (Loc t _) = access t


-- | Returns all subtrees; i.e. every node with all its decendents
subTrees :: BinaryTree a -> [BinaryTree a]
subTrees t = Nil : subTrees' t
  where
    subTrees' Nil                 = []
    subTrees' tt@(Internal l _ r) = tt : subTrees' l <> subTrees' r


-- | Splits the tree here, returns a pair (innerTree,outerTree)
splitTree             :: BinaryTreeZipper a -> (BinaryTree a, BinaryTree a)
splitTree (Loc t ctx) = let (Loc r _) = toRoot $ Loc Nil ctx
                        in (t, r)