{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}

-- | PrimitiveArray with 'Ix' keys.

module Data.PrimitiveArray.Ix where

import Data.Primitive
import Control.Monad.ST
import Control.Monad (liftM, forM_, zipWithM_, forM)
import Data.Primitive.Types
import qualified GHC.Arr as A

import Data.PrimitiveArray
import Data.PrimitiveArray.Internal



instance (Bounded a, A.Ix a, Prim b) => PrimArrayOps a b where
  data PrimArray a b = PaIxP !a !a {-# UNPACK #-} !ByteArray
  unsafeIndex (PaIxP lb ub arr) i = indexByteArray arr (A.unsafeIndex (lb,ub) i)
  assocs (pa@(PaIxP lb ub _)) = [(i, unsafeIndex pa i) | i<-A.range(lb,ub) ]
  fromAssocs l u z xs = runST $ do
    pam <- fromAssocsM l u z xs
    unsafeFreezeM pam
  bounds (PaIxP lb ub _) = (lb,ub)
  checkBounds (PaIxP lb ub _) i = A.inRange (lb,ub) i
  fromList l u xs = runST $ do
    pam <- fromListM l u xs
    unsafeFreezeM pam
  toList pa@(PaIxP lb ub _) = [unsafeIndex pa i | i <- A.range(lb,ub)]
  {-# INLINE unsafeIndex #-}
  {-# INLINE assocs #-}
  {-# INLINE fromAssocs #-}
  {-# INLINE bounds #-}
  {-# INLINE checkBounds #-}
  {-# INLINE fromList #-}
  {-# INLINE toList #-}

instance (Bounded a, A.Ix a, Prim b) => PrimArrayOpsM a b (ST s) where
  data PrimArrayM a b (ST s) = PaIxPM !a !a {-# UNPACK #-} !(MutableByteArray s)
  readM (PaIxPM lb ub marr) i = readByteArray marr (A.unsafeIndex (lb,ub) i)
  writeM (PaIxPM lb ub marr) i val = writeByteArray marr (A.unsafeIndex (lb,ub) i) val
  boundsM (PaIxPM lb ub _) = return (lb,ub)
  fromAssocsM lb ub z xs = do
    let l = A.rangeSize (lb,ub)
    pam <- PaIxPM lb ub `liftM` newWith l z
    forM_ xs $ uncurry (writeM pam)
    return pam
  unsafeFreezeM (PaIxPM lb ub marr) = do
    arr <- unsafeFreezeByteArray marr
    return $ PaIxP lb ub arr
  fromListM lb ub xs = do
    let l = A.rangeSize (lb,ub)
    pam <- PaIxPM lb ub `liftM` new l (undefined `asTypeOf` head xs)
    zipWithM_ (writeM pam) (A.range (lb,ub)) xs
    return pam
  toListM pam@(PaIxPM lb ub _) = forM (A.range (lb,ub)) (readM pam)
  {-# INLINE readM #-}
  {-# INLINE writeM #-}
  {-# INLINE boundsM #-}
  {-# INLINE fromAssocsM #-}
  {-# INLINE unsafeFreezeM #-}
  {-# INLINE fromListM #-}
  {-# INLINE toListM #-}