module Data.Vector.Fixed.Storable (
    
    Vec
  , Vec1
  , Vec2
  , Vec3
  , Vec4
  , Vec5
    
  , unsafeFromForeignPtr
  , unsafeToForeignPtr
  , unsafeWith
    
  , MVec(..)
    
  , Storable
  ) where
import Control.Monad.Primitive
import Control.DeepSeq (NFData(..))
import Data.Monoid           (Monoid(..))
import Data.Data
import Foreign.Ptr           (castPtr)
import Foreign.Storable
import Foreign.ForeignPtr
import Foreign.Marshal.Array ( advancePtr, copyArray, moveArray )
import GHC.ForeignPtr        ( ForeignPtr(..), mallocPlainForeignPtrBytes )
import GHC.Ptr               ( Ptr(..) )
import Prelude (Show(..),Eq(..),Ord(..),Num(..),Monad(..),IO,Int)
import Prelude ((++),(&&),(||),($),undefined,seq)
import Data.Vector.Fixed hiding (index)
import Data.Vector.Fixed.Mutable
import qualified Data.Vector.Fixed.Cont as C
newtype Vec n a = Vec (ForeignPtr a)
newtype MVec n s a = MVec (ForeignPtr a)
deriving instance Typeable Vec
deriving instance Typeable MVec
type Vec1 = Vec (S Z)
type Vec2 = Vec (S (S Z))
type Vec3 = Vec (S (S (S Z)))
type Vec4 = Vec (S (S (S (S Z))))
type Vec5 = Vec (S (S (S (S (S Z)))))
unsafeToForeignPtr :: Vec n a -> ForeignPtr a
unsafeToForeignPtr (Vec fp) = fp
unsafeFromForeignPtr :: ForeignPtr a -> Vec n a
unsafeFromForeignPtr = Vec
unsafeWith :: (Ptr a -> IO b) -> Vec n a -> IO b
unsafeWith f (Vec fp) = f (getPtr fp)
instance (Arity n, Storable a, Show a) => Show (Vec n a) where
  show v = "fromList " ++ show (toList v)
instance (Arity n, Storable a, NFData a) => NFData (Vec n a) where
  rnf = foldl (\r a -> r `seq` rnf a) ()
  
type instance Mutable (Vec n) = MVec n
instance (Arity n, Storable a) => MVector (MVec n) a where
  overlaps (MVec fp) (MVec fq)
    = between p q (q `advancePtr` n) || between q p (p `advancePtr` n)
    where
      between x y z = x >= y && x < z
      p = getPtr fp
      q = getPtr fq
      n = arity (undefined :: n)
  
  new = unsafePrimToPrim $ do
    fp <- mallocVector $ arity (undefined :: n)
    return $ MVec fp
  
  copy (MVec fp) (MVec fq)
    = unsafePrimToPrim
    $ withForeignPtr fp $ \p ->
      withForeignPtr fq $ \q ->
      copyArray p q (arity (undefined :: n))
  
  move (MVec fp) (MVec fq)
    = unsafePrimToPrim
    $ withForeignPtr fp $ \p ->
      withForeignPtr fq $ \q ->
      moveArray p q (arity (undefined :: n))
  
  unsafeRead (MVec fp) i
    = unsafePrimToPrim
    $ withForeignPtr fp (`peekElemOff` i)
  
  unsafeWrite (MVec fp) i x
    = unsafePrimToPrim
    $ withForeignPtr fp $ \p -> pokeElemOff p i x
  
instance (Arity n, Storable a) => IVector (Vec n) a where
  unsafeFreeze (MVec fp)   = return $ Vec  fp
  unsafeThaw   (Vec  fp)   = return $ MVec fp
  unsafeIndex  (Vec  fp) i
    = unsafeInlineIO
    $ withForeignPtr fp (`peekElemOff` i)
  
  
  
type instance Dim  (Vec  n) = n
type instance DimM (MVec n) = n
instance (Arity n, Storable a) => Vector (Vec n) a where
  construct  = constructVec
  inspect    = inspectVec
  basicIndex = index
  
  
  
instance (Arity n, Storable a) => VectorN Vec n a
instance (Arity n, Storable a, Eq a) => Eq (Vec n a) where
  (==) = eq
  
instance (Arity n, Storable a, Ord a) => Ord (Vec n a) where
  compare = ord
  
instance (Arity n, Storable a, Monoid a) => Monoid (Vec n a) where
  mempty  = replicate mempty
  mappend = zipWith mappend
  
  
instance (Arity n, Storable a) => Storable (Vec n a) where
  sizeOf    _ = arity (undefined :: n) * sizeOf (undefined :: a)
  alignment _ = alignment (undefined :: a)
  peek ptr = do
    arr@(MVec fp) <- new
    withForeignPtr fp $ \p ->
      moveArray p (castPtr ptr) (arity (undefined :: n))
    unsafeFreeze arr
  poke ptr (Vec fp)
    = withForeignPtr fp $ \p ->
      moveArray (castPtr ptr) p (arity (undefined :: n))
instance (Typeable n, Arity n, Storable a, Data a) => Data (Vec n a) where
  gfoldl       = C.gfoldl
  gunfold      = C.gunfold
  toConstr   _ = con_Vec
  dataTypeOf _ = ty_Vec
ty_Vec :: DataType
ty_Vec  = mkDataType "Data.Vector.Fixed.Primitive.Vec" [con_Vec]
con_Vec :: Constr
con_Vec = mkConstr ty_Vec "Vec" [] Prefix
mallocVector :: forall a. Storable a => Int -> IO (ForeignPtr a)
mallocVector size
  = mallocPlainForeignPtrBytes (size * sizeOf (undefined :: a))
getPtr :: ForeignPtr a -> Ptr a
getPtr (ForeignPtr addr _) = Ptr addr