{-# LANGUAGE FunctionalDependencies #-}
module Control.CanAquire(
      runAcquire
    , CanAquire(..)
    , HasIndex(..)
    , replaceByIndex, labelWithIndex
    , I
    ) where
import           Control.Monad.ST.Strict
import           Control.Monad.State.Strict
import           Data.Reflection
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
runAcquire         :: forall t a b. Traversable t
                   => (forall i. CanAquire i a => t i -> b)
                   -> t a -> b
runAcquire alg pts = reify v $ \px -> alg (coerceTS px ts)
  where
    (v,ts) = replaceByIndex pts
    coerceTS   :: proxy s -> t Int -> t (I s a)
    coerceTS _ = fmap I
      
class HasIndex i Int => CanAquire i a where
  
  aquire  :: i -> a
class HasIndex t i | t -> i where
  
  indexOf :: t -> i
replaceByIndex     :: forall t a. Traversable t => t a -> (V.Vector a, t Int)
replaceByIndex ts' = runST $ do
                               v <- MV.new n
                               t <- traverse (lbl v) ts
                               (,t) <$> V.unsafeFreeze v
  where
    (ts, n) = labelWithIndex ts'
    lbl         :: MV.MVector s' a -> (Int,a) -> ST s' Int
    lbl v (i,x) = MV.write v i x >> pure i
labelWithIndex :: Traversable t => t a -> (t (Int, a), Int)
labelWithIndex = flip runState 0 . traverse lbl
  where
    lbl   :: a -> State Int (Int,a)
    lbl x = do i <- get
               put $ i+1
               pure (i,x)
newtype I s a = I Int deriving (Eq, Ord, Enum)
instance Show (I s a) where
  showsPrec i (I j) = showsPrec i j
instance HasIndex (I s a) Int where
  indexOf (I i) = i
instance Reifies s (V.Vector a) => (I s a) `CanAquire` a where
  aquire (I i) = let v = reflect @s undefined in v V.! i