module Data.Registry.Class where

import Control.Monad
import Control.Monad.IO.Class
import Data.Maybe (isJust)
import qualified Data.Text as T
import qualified Data.Vector.Mutable as VM

-- | @IRegistry@ typeclass presents a registry interface.
-- The complexity /O(1)/ in the operations can be "amortized" complexity.
class IRegistry reg where
  -- | /O(1)/ Checking if the specified key exists
  has :: MonadIO m => reg v -> T.Text -> m Bool
  has reg k = fmap isJust $ reg !? k

  -- | /O(1)/ Indexing
  (!) :: MonadIO m => reg v -> T.Text -> m v
  reg ! k = fmap (\(Just a) -> a) $ reg !? k

  -- | /O(1)/ Safe indexing
  (!?) :: MonadIO m => reg v -> T.Text -> m (Maybe v)

  -- | /O(1)/ Update, raise an exception if the key does not exist.
  update :: MonadIO m => reg v -> T.Text -> (v -> m v) -> m ()
  update reg k f = reg ! k >>= \v -> f v >>= \v' -> write reg k v'

  -- | /O(1)/ Write, raise an exception if the key does not exist.
  write :: MonadIO m => reg v -> T.Text -> v -> m ()

  -- | /O(1)/ Adding a new value to the last position
  register :: MonadIO m => reg v -> T.Text -> v -> m ()

  -- | /O(n)/ Inserting a new value to the specified position (in the underlying vector)
  insert :: MonadIO m => reg v -> Int -> T.Text -> v -> m ()

  -- | /O(n)/ Deleting the specified value (this is a slow operation).
  delete :: MonadIO m => reg v -> T.Text -> m ()

  -- | /O(1)/ Get the underlying vector. Be careful: modifying the vector might cause a problem.
  asVec :: reg v -> VM.IOVector v

infixl 9 !
infixl 9 !?

-- | For-loop over the registry, ignoring the key order.
forV_ :: (MonadIO m, IRegistry reg) => reg v -> (v -> m ()) -> m ()
forV_ reg iter =
  let vec = asVec reg
  in  forM_ [0 .. VM.length vec - 1] $ \i -> liftIO (VM.read vec i) >>= iter

-- | For-loop over the registry with the index, ignoring the key order.
iforV_ :: (MonadIO m, IRegistry reg) => reg v -> (Int -> v -> m ()) -> m ()
iforV_ reg iter =
  let vec = asVec reg
  in  forM_ [0 .. VM.length vec - 1] $ \i -> liftIO (VM.read vec i) >>= iter i

-- | Modifying the item one by one, ignoring the key order.
modifyV_ :: (MonadIO m, IRegistry reg) => reg v -> (v -> m v) -> m ()
modifyV_ reg iter = iforV_ reg $ \i v -> do
  v' <- iter v
  liftIO $ VM.write (asVec reg) i v'