-- | Instances to allow 'Subword's to be used as index structures in
-- @ADPfusion@.

module ADP.Fusion.Base.Subword where

import Data.Vector.Fusion.Stream.Monadic (singleton,filter,enumFromStepN,map,unfoldr)
import Debug.Trace
import Prelude hiding (map,filter)

import Data.PrimitiveArray hiding (map)

import ADP.Fusion.Base.Classes
import ADP.Fusion.Base.Multi



instance RuleContext (Subword I) where
  type Context (Subword I) = InsideContext ()
  initialContext _ = IStatic ()
  {-# Inline initialContext #-}

instance RuleContext (Subword O) where
  type Context (Subword O) = OutsideContext (Int:.Int)
  initialContext _ = OStatic (0:.0)
  {-# Inline  initialContext #-}

instance RuleContext (Subword C) where
  type Context (Subword C) = ComplementContext
  initialContext _ = Complemented
  {-# Inline initialContext #-}

-- | The moving index @k@ in @Subword (i:.k)@.

newtype instance RunningIndex (Subword I) = RiSwI Int

-- | The moving indices @Inside (i:.j)@ and @Outside (k:.l)@ in order @i
-- j k l@.
--
-- TODO can we do with 2x Int?

data instance RunningIndex (Subword O) = RiSwO !Int !Int !Int !Int

-- | The indices @Subword (i:.j)@ in order @i j@.

data instance RunningIndex (Subword C) = RiSwC !Int !Int



-- | NOTE it seems that a static check within an @IVariable@ context
-- destroys fusion; maybe because of the outer flatten? We don't actually
-- need a static check anyway because the next flatten takes care of
-- conditional checks. @filter@ on the other hand, does work.
--
-- TODO test with and without filter using quickcheck
--
-- TODO shouldn't the new @staticCheck@ impl handle this?

instance (Monad m) => MkStream m S (Subword I) where
  mkStream S (IStatic ()) (Subword (_:.h)) (Subword (i:.j))
    -- = staticCheck (0<=i && i<=j)
    = filter (const $ 0<=i && i<=j)
    . singleton
    . ElmS $ RiSwI i
  mkStream S (IVariable ()) (Subword (_:.h)) (Subword (i:.j))
    -- = staticCheck (0<=i && i<=j)
    = filter (const $ 0<=i && i<=j && j<=h)
    . singleton
    . ElmS $ RiSwI i
  {-# Inline mkStream #-}

instance (Monad m) => MkStream m S (Subword O) where
  mkStream S (OStatic (di:.dj)) (Subword (_:.h)) (Subword (i:.j))
    = staticCheck (i==0 && j+dj==h) . singleton . ElmS $ RiSwO i j  i (j+dj)
  mkStream S (OFirstLeft (di:.dj)) (Subword (_:.h)) (Subword (i:.j))
    = let i' = i-di
      in  staticCheck (0 <= i' && i<=j && j+dj<=h) . singleton . ElmS $ RiSwO i' i' i' i'
  mkStream S (OLeftOf (di:.dj)) (Subword (_:.h)) (Subword (i:.j))
    = let i' = i-di
      in  staticCheck (0 <= i' && i<=j && j+dj<=h)
    $ map (\k -> ElmS $ RiSwO 0 k k j)
    $ enumFromStepN 0 1 (i'+1)
  mkStream S e _ _ = error $ show e ++ "maybe only inside syntactic terminals on the RHS of an outside rule?" -- TODO mostly because I'm not sure if that would be useful
  {-# Inline mkStream #-}

-- | 
--
-- TODO The @go@ here needs an explanation.

instance (Monad m) => MkStream m S (Subword C) where
  mkStream S Complemented (Subword (_:.h)) (Subword (i:.j))
    = map (\(k,l) -> ElmS $ RiSwC k l)
    $ unfoldr go (i,i)
    where go (k,l)
            | k >h || k >j = Nothing
            | l==h || l==j = Just ( (k,l) , (k+1,k+1) )
            | otherwise    = Just ( (k,l) , (k  ,l+1) )
          {-# Inline [0] go #-}
  {-# Inline mkStream #-}



instance
  ( Monad m
  , MkStream m S is
--  , Context (is:.Subword) ~ (Context is:.(InsideContext ()))
  ) => MkStream m S (is:.Subword I) where
  mkStream S (vs:.IStatic ()) (lus:.Subword (_:.h)) (ixs:.Subword(i:.j))
    = staticCheck (0<=i && i==j) -- && j<=h)
    . map (\(ElmS zi) -> ElmS (zi:.:RiSwI i))
    $ mkStream S vs lus ixs
  mkStream S (vs:.IVariable ()) (lus:.Subword (_:.h)) (ixs:.Subword (i:.j))
    = map (\(ElmS zi) -> ElmS (zi:.:RiSwI i))
    . staticCheck (0<=i && i<=j) -- filter (const $ 0<=i && i<=j && j<=h)
    $ mkStream S vs lus ixs
  {-# Inline mkStream #-}

instance (MinSize c) => TableStaticVar u c (Subword I) where
  tableStaticVar _ _ (IStatic   d) _ = IVariable d
  tableStaticVar _ _ (IVariable d) _ = IVariable d
  tableStreamIndex _ c _ (Subword (i:.j)) = subword i (j - minSize c)
  {-# INLINE [0] tableStaticVar   #-}
  {-# INLINE [0] tableStreamIndex #-}

-- | This instance is chosen if we consider an outside table (i.e.
-- a syntactic variable) in an outside index.
--
-- TODO @tableStreamIndex@ needs to be fixed

instance TableStaticVar (u O) c (Subword O) where
  tableStaticVar _ _ (OStatic  d) _ = OFirstLeft d
  tableStaticVar _ _ (ORightOf d) _ = OFirstLeft d
  tableStreamIndex _ c _ (Subword (i:.j)) = subword i j
  {-# INLINE [0] tableStaticVar   #-}
  {-# INLINE [0] tableStreamIndex #-}

-- | This instance is chosen if we consider an inside table (i.e.
-- a terminal symbol!) in an outside index.
--
-- TODO @tableStreamIndex@ needs to be fixed

instance TableStaticVar (u I) c (Subword O) where
  tableStaticVar _ _ (OStatic    d) _ = ORightOf d
  tableStaticVar _ _ (ORightOf   d) _ = ORightOf d
  tableStaticVar _ _ (OFirstLeft d) _ = OLeftOf d
  tableStaticVar _ _ (OLeftOf    d) _ = OLeftOf d
  tableStreamIndex _ c _ (Subword (i:.j)) = subword i j
  {-# INLINE [0] tableStaticVar   #-}
  {-# INLINE [0] tableStreamIndex #-}

instance TableStaticVar (u I) c (Subword C) where
  tableStaticVar _ _ _ _ = Complemented
  tableStreamIndex _ c _ (Subword (i:.j)) = subword i j
  {-# INLINE [0] tableStaticVar   #-}
  {-# INLINE [0] tableStreamIndex #-}

instance TableStaticVar (u O) c (Subword C) where
  tableStaticVar _ _ _ _ = Complemented
  tableStreamIndex _ c _ (Subword (i:.j)) = subword i j
  {-# INLINE [0] tableStaticVar   #-}
  {-# INLINE [0] tableStreamIndex #-}