{-# LANGUAGE UndecidableInstances #-}
module Data.Repa.Array.Meta.Window
        ( W          (..)
        , Array      (..)
        , Windowable (..)
        , windowed
        , entire
        , tail
        , init)
where
import Data.Repa.Array.Generic.Index
import Data.Repa.Array.Internals.Bulk
import Prelude hiding (length, tail, init)
#include "repa-array.h"


-- Windows --------------------------------------------------------------------
data W l 
        = Window 
        { windowStart   :: Index l
        , windowSize    :: Index l
        , windowInner   :: l }

deriving instance (Show l, Show (Index l)) => Show (W l)
deriving instance (Eq   l, Eq   (Index l)) => Eq   (W l)


-------------------------------------------------------------------------------
-- | Windowed arrays.
instance Layout l => Layout (W l) where
        data Name  (W l) = W (Name l)
        type Index (W l) = Index l

        name = W name

        create (W n) len  
         = let  inner   = create n len
           in   Window zeroDim (extent inner) inner

        extent    (Window _ sz _)  
                = sz

        toIndex   (Window _st _sz inner) ix  
                = toIndex inner ix              -- TODO: wrong, use offsets

        fromIndex (Window _st _sz inner) ix     -- TODO: wrong, use offsets
                = fromIndex inner ix

        {-# INLINE_ARRAY name      #-}
        {-# INLINE_ARRAY create    #-}
        {-# INLINE_ARRAY toIndex   #-}
        {-# INLINE_ARRAY extent    #-}
        {-# INLINE_ARRAY fromIndex #-}


deriving instance Eq   (Name l) => Eq   (Name (W l))
deriving instance Show (Name l) => Show (Name (W l))


-------------------------------------------------------------------------------
-- | Windowed arrays.
instance Bulk l a => Bulk (W l) a where
 data Array (W l) a             = WArray !(Index l) !(Index l) !(Array l a)
 layout (WArray st  sz inner)   = Window st sz (layout inner)
 index  (WArray st _  inner) ix = index inner (addDim st ix)
 {-# INLINE_ARRAY layout #-}
 {-# INLINE_ARRAY index  #-}


-- | Wrap a window around an exiting array.
windowed :: Index l -> Index l -> Array l a -> Array (W l) a
windowed start shape arr
        = WArray start shape arr
{-# INLINE_ARRAY windowed #-}


-- | Wrap a window around an existing array that encompases the entire array.
entire :: Bulk l a => Array l a -> Array (W l) a
entire arr
        = WArray zeroDim (extent $ layout arr) arr
{-# INLINE_ARRAY entire #-}


-------------------------------------------------------------------------------
-- | Class of array representations that can be windowed directly.
--
--   The underlying representation can encode the window, 
--   without needing to add a wrapper to the existing layout.
--
class Bulk l a    => Windowable l a where
 window :: Index l -> Index l -> Array l a -> Array l a

-- | Windows are windowable.
instance Bulk l a => Windowable (W l) a where
 window start _shape (WArray wStart wShape arr)
        = WArray (addDim wStart start) wShape arr
 {-# INLINE_ARRAY window #-}


-------------------------------------------------------------------------------
-- | O(1). Take the tail of an array, or `Nothing` if it's empty.
tail    :: (Windowable l a, Index l ~ Int)
        => Array l a -> Maybe (Array l a)
tail arr
        | length arr == 0       = Nothing
        | otherwise             = Just $! window 1 (length arr - 1) arr
{-# INLINE tail #-}


-- | O(1). Take the initial elements of an array, or `Nothing` if it's empty.
init    :: (Windowable l a, Index l ~ Int)
        => Array l a -> Maybe (Array l a)
init arr
        | length arr == 0       = Nothing
        | otherwise             = Just $! window 0 (length arr - 1) arr
{-# INLINE init #-}