module Data.Tree.Zipper
  ( TreePos
  , PosType, Empty, Full
  
  , before, after, forest, tree, label, parents
  
  , fromTree
  , fromForest
  , toForest
  , toTree
  
  , parent
  , root
  , prevSpace, prevTree, prev, first, spaceAt
  , nextSpace, nextTree, next, last
  , children, firstChild, lastChild, childAt
  
  , isRoot
  , isFirst
  , isLast
  , isLeaf
  , isContained
  , hasChildren
  
  , insert
  , delete
  , setTree
  , modifyTree
  , modifyLabel
  , setLabel
  ) where
import Data.Tree
import Prelude hiding (last)
data TreePos t a  = Loc
  { _content   :: t a        
  , _before    :: Forest a
  , _after     :: Forest a
  , _parents   :: [(Forest a, a, Forest a)]
  } deriving (Read,Show,Eq)
before         :: PosType t => TreePos t a -> Forest a
before          = _before
after          :: PosType t => TreePos t a -> Forest a
after           = _after
parents        :: PosType t => TreePos t a -> [(Forest a, a, Forest a)]
parents         = _parents
data Empty a    = E deriving (Read,Show,Eq)
newtype Full a  = F { unF :: Tree a } deriving (Read,Show,Eq)
class PosType t where
  _prev      :: TreePos t a -> Maybe (TreePos t a)
  _next      :: TreePos t a -> Maybe (TreePos t a)
  _forest    :: TreePos t a -> Forest a
instance PosType Full where
  _prev       = prevTree . prevSpace
  _next       = nextTree . nextSpace
  _forest loc = foldl (flip (:)) (tree loc : after loc) (before loc)
instance PosType Empty where
  _prev       = fmap prevSpace . prevTree
  _next       = fmap nextSpace . nextTree
  _forest loc = foldl (flip (:)) (after loc) (before loc)
prev    :: PosType t => TreePos t a -> Maybe (TreePos t a)
prev     = _prev
next     :: PosType t => TreePos t a -> Maybe (TreePos t a)
next      = _next
forest   :: PosType t => TreePos t a -> Forest a
forest    = _forest
parent :: PosType t => TreePos t a -> Maybe (TreePos Full a)
parent loc =
  case parents loc of
    (ls,a,rs) : ps -> Just
      Loc { _content  = F (Node a (forest loc))
          , _before   = ls
          , _after    = rs
          , _parents  = ps
          }
    [] -> Nothing
root :: TreePos Full a -> TreePos Full a
root loc = maybe loc root (parent loc)
prevSpace :: TreePos Full a -> TreePos Empty a
prevSpace loc = loc { _content = E, _after = tree loc : after loc }
prevTree :: TreePos Empty a -> Maybe (TreePos Full a)
prevTree loc =
  case before loc of
    t : ts -> Just loc { _content = F t, _before = ts }
    []     -> Nothing
nextSpace :: TreePos Full a -> TreePos Empty a
nextSpace loc = loc { _content = E, _before = tree loc : before loc }
nextTree :: TreePos Empty a -> Maybe (TreePos Full a)
nextTree loc =
  case after loc of
    t : ts -> Just loc { _content = F t, _after = ts }
    []     -> Nothing
children :: TreePos Full a -> TreePos Empty a
children loc =
  Loc { _content  = E
      , _before   = []
      , _after    = subForest (tree loc)
      , _parents  = (before loc, rootLabel (tree loc), after loc)
                  : parents loc
      }
first :: TreePos Empty a -> TreePos Empty a
first loc = loc { _content  = E
                , _before   = []
                , _after    = reverse (before loc) ++ after loc
                }
last :: TreePos Empty a -> TreePos Empty a
last loc = loc { _content = E
               , _before  = reverse (after loc) ++ before loc
               , _after   = []
               }
spaceAt :: Int -> TreePos Empty a -> TreePos Empty a
spaceAt n loc = loc { _content = E
                    , _before  = reverse as
                    , _after   = bs
                    }
  where (as,bs) = splitAt n (forest loc)
firstChild :: TreePos Full a -> Maybe (TreePos Full a)
firstChild = nextTree . children
lastChild :: TreePos Full a -> Maybe (TreePos Full a)
lastChild = prevTree . last . children
childAt :: Int -> TreePos Full a -> Maybe (TreePos Full a)
childAt n | n < 0 = const Nothing
childAt n         = nextTree . spaceAt n . children
fromTree :: Tree a -> TreePos Full a
fromTree t = Loc { _content = F t, _before = [], _after = [], _parents = [] }
fromForest :: Forest a -> TreePos Empty a
fromForest ts = Loc { _content = E, _before = [], _after = ts, _parents = [] }
toTree :: TreePos Full a -> Tree a
toTree loc = tree (root loc)
toForest :: PosType t => TreePos t a -> Forest a
toForest loc = case parent loc of
                 Nothing -> forest loc
                 Just p  -> toForest p 
isRoot :: PosType t => TreePos t a -> Bool
isRoot loc = null (parents loc)
isFirst :: PosType t => TreePos t a -> Bool
isFirst loc = null (before loc)
isLast :: PosType t => TreePos t a -> Bool
isLast loc = null (after loc)
isLeaf :: TreePos Full a -> Bool
isLeaf loc = null (subForest (tree loc))
isContained :: PosType t => TreePos t a -> Bool
isContained loc = not (isRoot loc)
hasChildren :: TreePos Full a -> Bool
hasChildren loc = not (isLeaf loc)
tree :: TreePos Full a -> Tree a
tree x = unF (_content x)
label :: TreePos Full a -> a
label loc = rootLabel (tree loc)
insert :: Tree a -> TreePos Empty a -> TreePos Full a
insert t loc = loc { _content = F t }
delete :: TreePos Full a -> TreePos Empty a
delete loc = loc { _content = E }
setTree :: Tree a -> TreePos Full a -> TreePos Full a
setTree t loc = loc { _content = F t }
modifyTree :: (Tree a -> Tree a) -> TreePos Full a -> TreePos Full a
modifyTree f loc = setTree (f (tree loc)) loc
modifyLabel :: (a -> a) -> TreePos Full a -> TreePos Full a
modifyLabel f loc = setLabel (f (label loc)) loc
setLabel :: a -> TreePos Full a -> TreePos Full a
setLabel v loc = modifyTree (\t -> t { rootLabel = v }) loc