{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}

{-# OPTIONS_GHC -Wall #-}
-- | A 'RingBuffer' implementation based on IntMaps.  Operations have the same
-- complexity as the underlying IntMap
module Data.RingBuffer.MapBuffer (
  Initializable (..)
 ,RingBuffer (..)
 ,MapBuffer (..)
)

where

import           Prelude hiding (length)

import           Data.RingBuffer.Class
import           Data.IntMap (IntMap)
import qualified Data.IntMap as M

data MapBuffer a = MB {-# UNPACK #-} !Int !(IntMap a) 
  deriving (Eq, Show, Ord)

type instance El (MapBuffer el) = el

instance Initializable (MapBuffer el) where
  {-# INLINE newInit #-}
  newInit el sz = MB 0 $ M.fromDistinctAscList $ map (,el) [0..sz-1]

instance RingBuffer (MapBuffer el) where
  {-# INLINE length #-}
  length (MB _ vec) = M.size vec
  {-# INLINE (!) #-}
  (MB pos vec) ! ix = vec M.! ((pos-ix-1) `mod` M.size vec)
  {-# INLINE push #-}
  push = pushE
  {-# INLINE slice #-}
  slice = sliceB

pushE :: MapBuffer a -> a -> MapBuffer a
pushE (MB pos vec) el = 
  let newPos = if pos == M.size vec - 1 then 0 else pos + 1
      vec' = M.insert pos el vec
  in  MB newPos vec'
{-# INLINE pushE #-}

sliceB :: MapBuffer a -> Int -> Int -> [a]
sliceB (MB pos vec) start num = -- V.toList $ V.slice (pos+start) num vec
  let ix1 = pos+start
      top = snd $ M.split (ix1-1) vec
  in  map snd . M.toAscList . fst $ M.split (ix1+num) top
{-# INLINE sliceB #-}