{-# LANGUAGE GADTs #-}
module Data.Tree.AVL.Static (
  AVLTree,
  Zipper,
  empty,
  size,
  insert,
  search,
  delete,
  value,
  begin,
  end,
  predecessor,
  successor,
) where

import Data.Tree.AVL.Static.Internal

-- |An empty 'AVLTree'.
--
-- /O(1)/.
empty :: AVLTree a
empty = T Nil 0

-- |The number of nodes of an 'AVLTree'.
--
-- /O(1)/.
size :: AVLTree a -> Integer
size (T _ k) = k

-- |Insert a value into an 'AVLTree'.
-- If the value already exists, does nothing.
--
-- /O(log n)/.
insert :: Ord a => a -> AVLTree a -> AVLTree a
insert x t = case zipTo x (unZip t) of
  Zipper Nil ctx -> insertUnbalancedAt (Balanced x Nil Nil) ctx
  _ -> t

-- |Search for a node with a given value. Returns a 'Zipper' pointing to
-- a subtree whose root has that value, or 'Nothing' if the value is not
-- in the tree.
--
-- /O(log n)/.
search :: Ord a => a -> AVLTree a -> Maybe (Zipper a)
search x t = case zipTo x (unZip t) of
  Zipper Nil _ -> Nothing
  z -> Just z

-- |Returns a 'Zipper' to the predecessor of a value in a tree. If the input
-- 'Zipper' points to the smallest element in the tree, returns 'Nothing'.
--
-- /O(log n)/.
predecessor :: Ord a => Zipper a -> Maybe (Zipper a)
predecessor = zipToPredecessor

-- |Returns a 'Zipper' to the successor of a value in a tree. If the input
-- 'Zipper' points to the greatest element in the tree, returns 'Nothing'.
--
-- /O(log n)/.
successor :: Ord a => Zipper a -> Maybe (Zipper a)
successor = zipToSuccessor

-- |Deletes a value from an 'AVLTree'. If the value does not exist in the tree,
-- does nothing.
--
-- /O(log n)/.
delete :: Ord a => a -> AVLTree a -> AVLTree a
delete x t = case zipTo x (unZip t) of
  Zipper Nil _ -> t
  z -> deleteBST z

-- |Returns a 'Zipper' to the smallest element in the tree, or 'Nothing' if the
-- tree is empty.
--
-- /O(log n)/.
begin :: AVLTree a -> Maybe (Zipper a)
begin t | size t == 0 = Nothing
        |otherwise = Just . zipToSmallest . unZip $ t

-- |Returns a 'Zipper' to the greatest element in the tree, or 'Nothing' if the
-- tree is empty.
--
-- /O(log n)/.
end :: AVLTree a -> Maybe (Zipper a)
end t | size t == 0 = Nothing
      | otherwise = Just . zipToGreatest . unZip $ t