module Data.Repa.Scalar.Box
        ( Box           (..)
        , box, unbox)
where
import qualified Data.Vector                    as V
import qualified Data.Vector.Unboxed            as U
import qualified Data.Vector.Generic            as G
import qualified Data.Vector.Generic.Mutable    as GM



-- | Strict, boxed wrapper for a value.
--
--   Useful as a default case when defining instances for polytypic 
--   data types.
newtype Box a
        = Box a
        deriving (Eq, Show)


-- | Put a value in a box.
box   :: a -> Box a
box !x = Box x
{-# INLINE box #-}


-- | Take the value from the box.
unbox :: Box a -> a
unbox (Box x) = x
{-# INLINE unbox #-}


instance Functor Box where
 fmap f (Box x) = Box (f x)
 {-# INLINE fmap #-}


-- Unboxed ----------------------------------------------------------------------------------------
-- Unboxed instance adapted from:
-- http://code.haskell.org/vector/internal/unbox-tuple-instances
--
data instance U.Vector (Box a)
  = V_Box
        {-# UNPACK #-} !Int
        !(V.Vector a)


data instance U.MVector s (Box a)
  = MV_Box
        {-# UNPACK #-} !Int
        !(V.MVector s a)


instance GM.MVector U.MVector (Box a) where

  basicLength (MV_Box n _as) = n
  {-# INLINE basicLength  #-}

  basicUnsafeSlice i m (MV_Box _n as)
   =  MV_Box m (GM.basicUnsafeSlice i m as)
  {-# INLINE basicUnsafeSlice  #-}

  basicOverlaps (MV_Box _n1 as1) (MV_Box _n2 as2)
   =  GM.basicOverlaps as1 as2
  {-# INLINE basicOverlaps  #-}

  basicUnsafeNew n
   = do as <- GM.basicUnsafeNew n
        return $ MV_Box n as
  {-# INLINE basicUnsafeNew  #-}

  basicUnsafeReplicate n (Box a)
   = do as <- GM.basicUnsafeReplicate n a
        return $ MV_Box n as
  {-# INLINE basicUnsafeReplicate  #-}

  basicUnsafeRead (MV_Box _n as) i
   = do v  <- GM.basicUnsafeRead as i
        return $ Box v
  {-# INLINE basicUnsafeRead  #-}

  basicUnsafeWrite (MV_Box _n as) i (Box a)
   = a `seq` GM.basicUnsafeWrite as i a
  {-# INLINE basicUnsafeWrite  #-}

  basicClear (MV_Box _n as)
   =    GM.basicClear as
  {-# INLINE basicClear  #-}

  basicSet   (MV_Box _n as) (Box a)
   =    GM.basicSet as a
  {-# INLINE basicSet  #-}

  basicUnsafeCopy (MV_Box _n1 as1) (MV_Box  _n2 as2)
   =    GM.basicUnsafeCopy as1 as2
  {-# INLINE basicUnsafeCopy  #-}

  basicUnsafeMove (MV_Box _n1 as1) (MV_Box _n2 as2)
   =    GM.basicUnsafeMove as1 as2
  {-# INLINE basicUnsafeMove  #-}

  basicUnsafeGrow (MV_Box n as) m
   = do as' <- GM.basicUnsafeGrow as m
        return $ MV_Box (m + n) as'
  {-# INLINE basicUnsafeGrow  #-}


instance G.Vector U.Vector (Box a) where

  basicUnsafeFreeze (MV_Box n as)
   = do as' <- G.basicUnsafeFreeze as
        return $ V_Box n as'
  {-# INLINE basicUnsafeFreeze  #-}

  basicUnsafeThaw (V_Box n as)
   = do as' <- G.basicUnsafeThaw as
        return $ MV_Box n as'
  {-# INLINE basicUnsafeThaw  #-}

  basicLength (V_Box n _as)
   = n
  {-# INLINE basicLength  #-}

  basicUnsafeSlice i m (V_Box _n as)
   = V_Box m (G.basicUnsafeSlice i m as)
  {-# INLINE basicUnsafeSlice  #-}

  basicUnsafeIndexM (V_Box _n as) i
   = do a <- G.basicUnsafeIndexM as i
        return (Box a)
  {-# INLINE basicUnsafeIndexM  #-}

  basicUnsafeCopy (MV_Box _n1 as1) (V_Box _n2 as2)
   =    G.basicUnsafeCopy as1 as2
  {-# INLINE basicUnsafeCopy  #-}

  elemseq _ (Box a)
      = G.elemseq (undefined :: V.Vector a) a
  {-# INLINE elemseq  #-}