{-# LANGUAGE GADTs #-}

module Data.MultiIORef (
    MultiIORef(..)
  , loosenIORef
  , makeMultiIORef
  , zoomMultiIORef
  , newMultiIORef
  , readMultiIORef
  , readMultiIORefList
  , readMultiIORefHead
  , modifyMultiIORef, modifyMultiIORef'
  , writeMultiIORef
) where

import Data.IORef.Zoom

import Control.Lens 

data MultiIORef a where
   MultiIORef :: IORef x -> ATraversal' x a -> MultiIORef a

loosenIORef :: IORef a -> MultiIORef a
loosenIORef v = MultiIORef v id

makeMultiIORef :: ATraversal' x a -> IORef x -> MultiIORef a
makeMultiIORef = flip MultiIORef

zoomMultiIORef :: ATraversal' a b -> MultiIORef a -> MultiIORef b
zoomMultiIORef t1 (MultiIORef v t2) = MultiIORef v . confusing $ cloneTraversal t2 . cloneTraversal t1

newMultiIORef :: a -> IO (MultiIORef a)
newMultiIORef a = MultiIORef <$> newIORef a <*> pure id

readMultiIORef :: Monoid a => MultiIORef a -> IO a
readMultiIORef (MultiIORef v t) = (^. cloneTraversal t) <$> readIORef v 

readMultiIORefList :: MultiIORef a -> IO [a]
readMultiIORefList (MultiIORef v t) = (^.. cloneTraversal t) <$> readIORef v

readMultiIORefHead :: MultiIORef a -> IO (Maybe a)
readMultiIORefHead (MultiIORef v t) = (^? cloneTraversal t) <$> readIORef v

modifyMultiIORef :: MultiIORef a -> (a -> a) -> IO ()
modifyMultiIORef (MultiIORef v t) f = modifyIORef v $ cloneTraversal t %~ f

modifyMultiIORef' :: MultiIORef a -> (a -> a) -> IO ()
modifyMultiIORef' (MultiIORef v t) f = modifyIORef' v $ cloneTraversal t %~ f

writeMultiIORef :: MultiIORef a -> a -> IO ()
writeMultiIORef (MultiIORef v t) a = modifyIORef' v $ cloneTraversal t .~ a