module Control.Concurrent.TFile( 
              TFile,
              
              newEmptyIO,
              newIO,
              new,
              
              read,
              write,
              delete,
              isEmpty
            )
where
import Control.Concurrent.TBox.Class
import Control.Concurrent.TBox.Operations
import Data.Binary
import Data.Typeable
import Control.Concurrent.AdvSTM.TMVar
import Control.Concurrent.AdvSTM
import Control.Concurrent.AdvSTM.TVar
import Control.Monad
import Control.Exception 
import System.Directory
import System.IO.Error hiding(catch)
import System.IO.Cautious(writeFileL)
import Prelude hiding(lookup,catch,null,read,readIO,writeFile)
data TFile a = TF 
  { filepath  :: FilePath
  , tfileTVar :: TVar (Maybe a)
  , dirtyTVar :: TVar Bool
  , fileLock  :: TMVar () 
  }
  deriving(Typeable)
newEmptyIO :: Binary a => FilePath -> IO (TFile a)
newEmptyIO fp = 
  TF fp `liftM` newTVarIO Nothing 
           `ap` (newTVarIO =<< doesFileExist fp)
           `ap` newTMVarIO ()
newIO :: Binary a => FilePath -> a -> IO (TFile a)
newIO fp = atomically . new fp
new :: Binary a => FilePath -> a -> AdvSTM (TFile a)
new fp a = do
  tbox <- TF fp `liftM` newTVar (Just a) 
                   `ap` newTVar False 
                   `ap` newTMVar ()
  write tbox a
  return tbox
instance Binary a => TBox TFile a where
  writeSTM tbox = writeTVar (tfileTVar tbox) . Just 
  writeIO (TF fp _ _ lock) = withTMVar lock . const . writeFileL fp . encode 
  readSTM = readTVar . tfileTVar 
  readIO (TF fp _ _ lock) = (do
    a <- withTMVar lock $ const $ decodeFile fp
    return $ Just a)
    `catch` \(e::IOException) -> 
      if isDoesNotExistError e then return Nothing else throw e
  deleteSTM tbox = writeTVar (tfileTVar tbox) Nothing
  deleteIO (TF fp _ _ lock) = withTMVar lock $ const $ removeFile fp
    `catch` \(e::IOException) -> unless (isDoesNotExistError e) $ throw e
  isDirty  = readTVar  . dirtyTVar 
  setDirty = writeTVar . dirtyTVar 
withTMVar :: TMVar a -> (a -> IO b) -> IO b
withTMVar tmvar ioac = do
  a <- atomically $ takeTMVar tmvar
  res <- ioac a
  atomically $ putTMVar tmvar a
  return res