-----------------------------------------------------------------------------
-- |
-- Module      :  Data.TStorage
-- Copyright   :  Peter Robinson 2009
-- License     :  LGPL
--
-- Maintainer  :  Peter Robinson <thaldyron@gmail.com>
-- Stability   :  experimental
-- Portability :  non-portable (requires STM)
--
-- Provides a high level interface to 'Data.TMap'. This module was inspired
-- by the TCache package, (C) Alberto Gomez Corona.
--
-- The essential difference to the low level functions in 'Data.TMap' is 
-- that this interface assumes that the stored type is an instance of 
-- 'HasKey', allowing partially filled (i.e. complete enough for deducing 
-- the key) values to be passed to the interface functions. 
-----------------------------------------------------------------------------

module Data.TStorage( TMap,
                      newTMapIO,
                      add,
                      tryComplete,
                      complete,
                      remove,
                      removeByKey,
                      apply,
--                      purgeTMap,
                      purgeTMapIO,
                      HasKey(key),
                    )
                    
where
import Control.Concurrent.AdvSTM
import Control.Exception 
import Prelude hiding (lookup,catch)

import qualified Data.TMap.Backend as B
import qualified Data.CacheStructure as C

import qualified Data.Edison.Assoc as M

import Data.TMap
import Data.HasKey(HasKey(key))


-- | Adds a new element to the map. The key is automatically deduced by the
-- 'HasKey' instantiation.
add :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k, B.Backend k a b, C.CacheStructure c k
       , HasKey a k) 
    => a -> TMap map k a b c -> stm () 
add a = insert (key a) a


-- | Tries to fill a partially initialized value with data from the TMap. Returns
-- 'Nothing' if the TMap does not contain a corresponding entry. 
tryComplete :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k,B.Backend k a b, C.CacheStructure c k
               , HasKey a k) 
            => a -> TMap map k a b c -> stm (Maybe a)
tryComplete a = lookup (key a)

-- | Fills a partially initialized value with data from the TMap. Throws
-- an 'EntryNotFound' exception if there is no corresponding entry.
complete :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k, B.Backend k a b, C.CacheStructure c k
            , HasKey a k) 
         => a -> TMap map k a b c -> stm a
complete a tmap = do
  mval <- tryComplete a tmap
  case mval of
    Just val -> return val
    Nothing  -> throw EntryNotFound

-- | Removes the element from the map.
remove :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k, B.Backend k a b, C.CacheStructure c k
          , HasKey a k) 
       => a -> TMap map k a b c -> stm ()
remove a = delete (key a) 

-- | Removes the entry that has the supplied key.
removeByKey :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k, B.Backend k a b, C.CacheStructure c k
             , HasKey a k) 
            => k -> TMap map k a b c -> stm ()
removeByKey = delete 

-- | Applies a function to an element that might be only partially initialized.
apply :: (M.FiniteMapX map k, MonadAdvSTM stm, Ord k, B.Backend k a b, C.CacheStructure c k
         , HasKey a k) 
       => (a -> a) -> a -> TMap map k a b c -> stm a
apply f a tmap = do
  adjust f (key a) tmap
  complete a tmap