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

{- |
   Module     : Data.Tree.NavigatableTree.XPathAxis
   Copyright  : Copyright (C) 2010 Uwe Schmidt
   License    : MIT

   Maintainer : Uwe Schmidt (uwe\@fh-wedel.de)
   Stability  : experimental
   Portability: portable

   Navigatable trees need to have operations to move up, down, left and right.
   With these elementary operations, the XPath axises can be defined.
-}

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

module Data.Tree.NavigatableTree.XPathAxis
where

import Data.Maybe               ( maybeToList )
import Data.Tree.NavigatableTree.Class

import Control.Arrow            ( (>>>) )
import Control.Monad            ( (>=>) )

-- ------------------------------------------------------------
--
-- mothers little helpers

-- | collect all trees by moving into one direction, starting tree is included

maybeStar               :: (a -> Maybe a) -> (a -> [a])
maybeStar f x            = x : maybe [] (maybeStar f) (f x)

-- | collect all trees by moving into one direction, starting tree is not included

maybePlus               :: (a -> Maybe a) -> (a -> [a])
maybePlus f x           =      maybe [] (maybeStar f) (f x)

{-# INLINE maybePlus #-}

-- ------------------------------------------------------------
-- XPath axis

-- | XPath axis: parent

parentAxis              :: NavigatableTree t => t a -> [t a]
parentAxis              = maybeToList . mvUp
{-# INLINE parentAxis #-}

-- | XPath axis: ancestor

ancestorAxis            :: NavigatableTree t => t a -> [t a]
ancestorAxis            = maybePlus mvUp
{-# INLINE ancestorAxis #-}

-- | XPath axis: ancestor or self

ancestorOrSelfAxis      :: NavigatableTree t => t a -> [t a]
ancestorOrSelfAxis      = maybeStar mvUp
{-# INLINE ancestorOrSelfAxis #-}

-- | XPath axis: child

childAxis               :: NavigatableTree t => t a -> [t a]
childAxis               = (mvDown >>> maybeToList) >=> maybeStar mvRight
{-# INLINE childAxis #-}

-- | XPath axis: descendant

descendantAxis          :: NavigatableTree t => t a -> [t a]
descendantAxis          = descendantOrSelfAxis >>> tail
{-# INLINE descendantAxis #-}

-- | XPath axis: descendant or self

descendantOrSelfAxis    :: NavigatableTree t => t a -> [t a]
descendantOrSelfAxis    = visit []
    where
    visit  k t          = t : maybe k (visit' k) (mvDown t)
    visit' k t          = visit (maybe k (visit' k) (mvRight t)) t

-- | not an official XPath axis but useful: reverse descendant or self, used in preceding axis

revDescendantOrSelfAxis :: NavigatableTree t => t a -> [t a]
revDescendantOrSelfAxis t
                        = t : concatMap revDescendantOrSelfAxis (reverse $ childAxis t)

-- | XPath axis: following sibling

followingSiblingAxis    :: NavigatableTree t => t a -> [t a]
followingSiblingAxis    = maybePlus mvRight
{-# INLINE followingSiblingAxis #-}

-- | XPath axis: preceeding sibling

precedingSiblingAxis    :: NavigatableTree t => t a -> [t a]
precedingSiblingAxis    = maybePlus mvLeft
{-# INLINE precedingSiblingAxis #-}

-- | XPath axis: self

selfAxis                :: NavigatableTree t => t a -> [t a]
selfAxis                = (:[])
{-# INLINE selfAxis #-}

-- | XPath axis: following

followingAxis           :: NavigatableTree t => t a -> [t a]
followingAxis           = ancestorOrSelfAxis >=> followingSiblingAxis >=> descendantOrSelfAxis

-- | XPath axis: preceding

precedingAxis           :: NavigatableTree t => t a -> [t a]
precedingAxis           = ancestorOrSelfAxis >=> precedingSiblingAxis >=> revDescendantOrSelfAxis

-- | move to the root

mvToRoot                :: NavigatableTree t => t a -> t a
mvToRoot                = ancestorOrSelfAxis >>> last
{-# INLINE mvToRoot #-}

isAtRoot                :: NavigatableTree t => t a -> Bool
isAtRoot                = null . ancestorAxis
{-# INLINE isAtRoot #-}

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