module ListZipper ( ListZipper(..)
                  , focus
                  , left
                  , right
                  , reset
                  , fromList
                  , toList
                  , next
                  , atEnd 
                  , at
                  )
where
import Data.Maybe
import Data.Monoid
import Data.Foldable (Foldable,foldMap)


data ListZipper a = LZ [a] (Maybe a) [a]  deriving (Show,Eq,Ord)


left  (LZ xs _ _) = xs
focus (LZ _ x _)  = x
right (LZ _ _ ys) = ys

fromList []     = LZ [] Nothing [] 
fromList (x:xs) = LZ [] (Just x) xs

toList (LZ xs (Just x) ys) = reverse xs ++ [x] ++ ys
toList (LZ xs Nothing [])  = reverse xs

next :: ListZipper a -> ListZipper a
next = nextWith id id
nextWith f g (LZ lxs (Just x) rxs)  =
    case rxs of
      y:ys -> LZ (f x:lxs) (Just (g y)) ys
      []   -> LZ (f x:lxs) Nothing []

atEnd = isNothing . focus

reset (LZ [] (Just y) ys)     = LZ [] (Just y) ys
reset (LZ [] Nothing [])      = LZ [] Nothing []
reset (LZ xs (Just y) ys) = LZ [] (Just z) (zs++[y]++ys)
    where z:zs = reverse xs                    
reset (LZ xs Nothing [])  = LZ [] (Just z) zs
    where z:zs = reverse xs

from :: (Monoid m) => Maybe m -> m
from Nothing  = mempty
from (Just x) = x

index 1 (x:xs)  = Just x
index i []  = Nothing
index i (x:xs) = index (i-1) xs

at :: (Monoid m) =>  ListZipper m -> Int -> m
at  z 0 = from . focus $ z
at  z i | i < 0 = from .  index (negate i) . left  $ z
at  z i         = from .  index i          . right $ z

instance Functor ListZipper where
    fmap f (LZ ls x rs) = LZ (fmap f ls) (fmap f x) (fmap f rs) 
instance Foldable ListZipper where
    foldMap f (LZ ls x rs) = mconcat $ fmap f (reverse ls) 
                             ++ [from $ fmap f x] ++ fmap f rs