{-# Language MagicHash #-}

module ADP.Fusion.Core.Multi where

import qualified Data.Vector.Fusion.Stream.Monadic as S
import           Data.Vector.Fusion.Stream.Monadic
import           Data.Strict.Tuple
import           Data.Proxy
import           Prelude hiding (map)
import           GHC.Exts
import           Debug.Trace

import           Data.PrimitiveArray.Index.Class hiding (map)

import           ADP.Fusion.Core.Classes
import           ADP.Fusion.Core.TyLvlIx



-- * Multi-dimensional extension

-- | Terminates a multi-dimensional terminal symbol stack.

data M = M
  deriving (Eq,Show)

infixl 2 :|

-- | Terminal symbols are stacked together with @a@ tails and @b@ head.

data TermSymbol a b = a :| b
  deriving (Eq,Show)

instance Build (TermSymbol a b)

-- | Extracts the type of a multi-dimensional terminal argument.

type family   TermArg x :: *
type instance TermArg M                = Z
type instance TermArg (TermSymbol a b) = (TermArg a) :. (TermArg b)

instance (Element ls i) => Element (ls :!: TermSymbol a b) i where
  data Elm (ls :!: TermSymbol a b) i = ElmTS !(TermArg (TermSymbol a b)) !(RunningIndex i) !(Elm ls i)
  type Arg (ls :!: TermSymbol a b)   = Arg ls :. TermArg (TermSymbol a b)
  getArg (ElmTS a _ ls) = getArg ls :. a
  getIdx (ElmTS _ i _ ) = i
  {-# INLINE getArg #-}
  {-# INLINE getIdx #-}

deriving instance (Show i, Show (RunningIndex i), Show (TermArg (TermSymbol a b)), Show (Elm ls i)) => Show (Elm (ls :!: TermSymbol a b) i)

type instance LeftPosTy (ps :. p) (TermSymbol a b) (is:.i) = (LeftPosTy ps a is) :. (LeftPosTy p b i)

instance
  ( Monad m
  , MkStream m posLeft ls i
  , Element ls i
  , TermStaticVar pos (TermSymbol a b) i
  , TermStream m pos (TermSymbol a b) (Elm ls i) i
  , posLeft ~ LeftPosTy pos (TermSymbol a b) i
  ) => MkStream m pos (ls :!: TermSymbol a b) i where
  mkStream Proxy (ls :!: ts) grd lu i
    = map (\(TState sS ii ee) -> ElmTS ee ii sS)
    . termStream (Proxy  Proxy pos) ts lu i
    . map (\s -> TState s RiZ Z)
    $ mkStream (Proxy  Proxy posLeft)
               ls
               (termStaticCheck (Proxy  Proxy pos) ts lu i grd)
               lu (termStreamIndex (Proxy  Proxy pos) ts i)
  {-# Inline mkStream #-}

-- | 

type instance LeftPosTy Z M Z = Z

instance Monad m => MkStream m Z S Z where
  -- mkStream Proxy S grd ZZ Z = S.filter (const $ isTrue# grd) $ S.singleton $ ElmS RiZ
  mkStream Proxy S grd ZZ Z = staticCheck# grd $ S.singleton $ ElmS RiZ
  {-# Inline mkStream #-}

-- | For multi-dimensional terminals we need to be able to calculate how the
-- static/variable signal changes and if the index for the inner part needs to
-- be modified.

class TermStaticVar pos sym ix where
--  termStaticVar   ∷ sym → Context i → i → Context i
  termStreamIndex  Proxy pos  sym  ix  ix
  termStaticCheck  Proxy pos  sym  LimitType ix  ix  Int#  Int#

instance TermStaticVar pos M Z where
  termStreamIndex Proxy M Z = Z
  termStaticCheck Proxy M _ Z grd = grd
  {-# INLINE [0] termStreamIndex #-}
  {-# INLINE [0] termStaticCheck #-}

instance
  ( TermStaticVar ps ts is
  , TermStaticVar p  t  i
  ) => TermStaticVar (ps:.p) (TermSymbol ts t) (is:.i) where
  termStreamIndex Proxy (ts:|t) (is:.i) = termStreamIndex (Proxy  Proxy ps) ts is :. termStreamIndex (Proxy  Proxy p) t i
  termStaticCheck Proxy (ts:|t) (us:..u) (is:.i) grd = termStaticCheck (Proxy  Proxy ps) ts us is (termStaticCheck (Proxy  Proxy p) t u i grd)
  {-# INLINE [0] termStreamIndex #-}
  {-# INLINE [0] termStaticCheck #-}

--instance RuleContext Z where
type instance InitialContext Z = Z

--instance (RuleContext is, RuleContext i) => RuleContext (is:.i) where
type instance InitialContext (is:.i) = InitialContext is:.InitialContext i

class TableStaticVar pos minSize tableIx ix where
  tableStreamIndex
     Proxy pos
    -- ^ provide type-level information on if we are currently static/variable/
    -- etc
     minSize
    -- ^ Information on the minimal size of the corresponding table.
     LimitType tableIx
    -- ^ provide type-level information on the index structure of the table we
    -- are looking at. This index structure might well be different than the
    -- @ix@ index we use in the grammar.
     ix
    -- ^ current right-most index
     ix
    -- ^ right-most index for symbol to the left of us

-- | Index "0" for multi-dimensional syntactic variables.

instance TableStaticVar pos Z tableIx Z where
  tableStreamIndex Proxy Z _ Z = Z
  {-# INLINE [0] tableStreamIndex #-}

instance
  ( TableStaticVar ps cs us is
  , TableStaticVar p  c  u  i
  )
   TableStaticVar (ps:.p) (cs:.c) (us:.u) (is:.i) where
  tableStreamIndex Proxy (cs:.c) (us:..u) (is:.i)
    =  tableStreamIndex (Proxy  Proxy ps) cs us is
    :. tableStreamIndex (Proxy  Proxy p ) c  u  i
  {-# INLINE [0] tableStreamIndex #-}


data TermState s i e = TState
  { tS  :: !s
    -- ^ state coming in from the left
  , iIx :: !(RunningIndex i)
    -- ^ @I/C@ building up state to hand over to next symbol
  , eTS :: !e
    -- ^ element data
  }

class TermStream m pos t s i where
  termStream
     Proxy pos
     t
     LimitType i
     i
     Stream m (TermState s Z Z)
     Stream m (TermState s i (TermArg t))

instance (Monad m) => TermStream m pos M s Z where
  termStream Proxy M ZZ Z = id
  {-# Inline termStream #-}

-- |
--
-- TODO need @t -> ElmType t@ type function
--
-- TODO need to actually return an @ElmType t@ can do that instead of
-- returning @u@ !!!

addTermStream1
   forall m pos t s i
  . ( Monad m
    , TermStream m (Z:.pos) (TermSymbol M t) (Elm (Term1 s) (Z:.i)) (Z:.i)
    )
   Proxy pos
   t
   LimitType i
   i
   Stream m s
   Stream m (s,TermArg t,RunningIndex i)
addTermStream1 Proxy t u i
  = map (\(TState (ElmTerm1 sS) (RiZ:.:ii) (Z:.ee)) -> (sS,ee,ii))
  . termStream (Proxy  Proxy (Z:.pos)) (M:|t) (ZZ:..u) (Z:.i)
  . map (\s -> TState (elmTerm1 s i) RiZ Z)
{-# Inline addTermStream1 #-}

newtype Term1 s = Term1 s

elmTerm1 :: s -> i -> Elm (Term1 s) (Z:.i)
elmTerm1 s _ = ElmTerm1 s
{-# Inline elmTerm1 #-}

instance (s ~ Elm x0 i, Element x0 i) => Element (Term1 s) (Z:.i) where
  newtype Elm (Term1 s) (Z:.i) = ElmTerm1 s
  getIdx (ElmTerm1 s) = RiZ :.: getIdx s
  {-# Inline getIdx #-}

-- | @Term MkStream@ context
--
-- TODO prepare for deletion

--type TermMkStreamContext m (pos ∷ k) ls t i
--  = ( Monad m
--    , MkStream m pos ls i
--    , TermStream m pos (TermSymbol M t) (Elm (Term1 (Elm ls i)) (Z:.i)) (Z:.i)
--    , Element ls i
--    , TermStaticVar pos t i
--    )

-- | @Term TermStream@ context

type TermStreamContext m (pos  k) ts s x0 sixty is i
  = ( Monad m
    , TermStream m pos ts s is
    , GetIndex (RunningIndex sixty) (RunningIndex (is:.i))
    , GetIx (RunningIndex sixty) (RunningIndex (is:.i)) ~ (RunningIndex i)
    , Element x0 sixty
    , s ~ Elm x0 sixty
    )

-- | Shorthand for proxifying @getIndex@

type PRI is i = Proxy (RunningIndex (is:.i))