{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
module Sound.SC3.Server.Allocator.SimpleAllocator (
    SimpleAllocator
  , cons
) where

import           Control.Failure (Failure, failure)
import           Sound.SC3.Server.Allocator
import           Sound.SC3.Server.Allocator.Range (Range)
import qualified Sound.SC3.Server.Allocator.Range as Range

data SimpleAllocator i =
    SimpleAllocator
        {-# UNPACK #-}!(Range i)
        {-# UNPACK #-}!Int
                      !i
        deriving (Eq, Show)

cons :: Range i -> SimpleAllocator i
cons r = SimpleAllocator r 0 (Range.begin r)

_alloc :: (Enum i, Ord i, Monad m) => SimpleAllocator i -> m (i, SimpleAllocator i)
_alloc (SimpleAllocator r n i) =
    let i' = succ i
    in return (i, SimpleAllocator r (n+1) (if i' >= Range.end r then Range.begin r else i'))

_free :: (Failure AllocFailure m) => i -> SimpleAllocator i -> m (SimpleAllocator i)
_free _ (SimpleAllocator r n i) =
    let n' = n-1
    in if n' < 0
       then failure InvalidId
       else return (SimpleAllocator r n' i)

_statistics :: Integral i => SimpleAllocator i -> Statistics
_statistics (SimpleAllocator r n _) =
    let k = fromIntegral (Range.size r)
    in Statistics {
        numAvailable = k
      , numFree = k - n
      , numUsed = n }

instance (Integral i) => IdAllocator (SimpleAllocator i) where
    type Id (SimpleAllocator i) = i
    alloc                       = _alloc
    free                        = _free
    statistics                  = _statistics