-- | Insert an edge into a set. @X -> Y e@ with @e == Edge@ extends @Y@
-- with the edge partially overlapping @Y@.
--
-- The semantic meaning of the overlap depends on what the @k@ type in @BS1
-- k i@ is. For @First@, the edge will go from @First@ in @X@ to @First@ in
-- the smaller @Y@.
--
-- TODO @X -> e X@ vs @X -> X e@.
--
-- Sidenote: can we actually have @X -> Y Z@ with @Set1@ structures?
-- I don't think so, at least not easily, since the boundary between @Y Z@
-- is unclear.

module ADP.Fusion.Term.Edge.Set1 where

import Data.Bits
import Data.Strict.Tuple
import Data.Vector.Fusion.Stream.Monadic hiding (flatten)
import Debug.Trace
import Prelude hiding (map,filter)

import ADP.Fusion.Core
import Data.Bits.Ordered
import Data.PrimitiveArray hiding (map)

import ADP.Fusion.Core.Set1
import ADP.Fusion.Term.Edge.Type



instance
  ( TmkCtx1 m ls Edge (BS1 k t)
  ) => MkStream m (ls :!: Edge) (BS1 k t) where
  mkStream (ls :!: Edge) sv us is
    = map (\(ss,ee,ii) -> ElmEdge ee ii ss)
    . addTermStream1 Edge sv us is
    $ mkStream ls (termStaticVar Edge sv is) us (termStreamIndex Edge sv is)
  {-# Inline mkStream #-}

-- | We need to separate out the two cases of having @BS1 First@ and @BS1
-- Last@ as this changes how we fill the Edge.
--
-- TODO separate out these cases into an Edge-Choice class ...

instance
  ( TstCtx m ts s x0 i0 is (BS1 k I)
  , EdgeFromTo k
  ) => TermStream m (TermSymbol ts Edge) s (is:.BS1 k I) where
  -- Begin the edge on @First == b@, and end it somewhere in the set.
  termStream (ts:|Edge) (cs:.IStatic r) (us:.u) (is:.BS1 i (Boundary newNode))
    = map (\(TState s ii ee) ->
        let RiBs1I (BS1 cset (Boundary setNode)) = getIndex (getIdx s) (Proxy :: PRI is (BS1 k I))
        in  TState s (ii:.:RiBs1I (BS1 i (Boundary newNode)))
                     (ee:.edgeFromTo (Proxy :: Proxy k) (SetNode setNode) (NewNode newNode)) )
    . filter (\(TState s ii ee) ->
        let RiBs1I (BS1 cset (Boundary setNode)) = getIndex (getIdx s) (Proxy :: PRI is (BS1 k I))
        in  popCount cset >= 1)
    . termStream ts cs us is
    . staticCheck (popCount i >= 2)
  -- Begin the edge somewhere, because in the variable case we do not end
  -- on @b@
  termStream (ts:|Edge) (cs:.IVariable r) (us:.u) (is:.BS1 i b)
    = flatten mk step . termStream ts cs us is
          -- get us the inner set, build an edge @avail -> to@
    where mk tstate@(TState s ii ee) =
            let RiBs1I (BS1 cset (Boundary setNode)) = getIndex (getIdx s) (Proxy :: PRI is (BS1 k I))
                avail = activeBitsL $ (i .&. complement cset) `clearBit` getBoundary b
            in  return $ (tstate,cset,setNode,avail)
          -- in @X -> Y e Z@, @e == Edge@ will only be active, if @Y@ has
          -- at least one active bit. This means that @X -> e ...@ will
          -- never be active.
          step (_,_,_,[]) = return $ Done
          step (TState s ii ee,cset,setNode,(newNode:xs))
            | setNode < 0  = error "Edge/Set1: source boundary is '-1'. Move all terminals to the right of syntactic variables!"
            | otherwise =
              let ix = RiBs1I $ BS1 (cset `setBit` newNode) (Boundary newNode)
              in  return $ Yield (TState s (ii:.:ix) (ee:.edgeFromTo (Proxy :: Proxy k) (SetNode setNode) (NewNode newNode)))
                                 (TState s ii ee,cset,setNode,xs)
          {-# Inline [0] mk   #-}
          {-# Inline [0] step #-}
  {-# Inline termStream #-}

-- |
--
-- TODO move to definition of 'Edge'

class EdgeFromTo k where
  edgeFromTo :: Proxy k -> SetNode -> NewNode -> (From:.To)

newtype SetNode = SetNode Int

newtype NewNode = NewNode Int

-- | In case our sets have a @First@ boundary, then we always point from
-- the boundary "into" the set. Hence @SetNode == To@ and @NewNode ==
-- From@.

instance EdgeFromTo First where
  edgeFromTo Proxy (SetNode to) (NewNode from) = From from :. To to
  {-# Inline edgeFromTo #-}

-- | And if the set has a @Last@ boundary, then we point from somewhere in
-- the set @To@ the @NewNode@, which is @Last@.

instance EdgeFromTo Last where
  edgeFromTo Proxy (SetNode from) (NewNode to) = From from :. To to
  {-# Inline edgeFromTo #-}

instance TermStaticVar Edge (BS1 k I) where
  termStaticVar   _ (IStatic   d) _ = IVariable $ d+1
  termStaticVar   _ (IVariable d) _ = IVariable $ d+1
  termStreamIndex _ _  ix = ix
  {-# Inline [0] termStaticVar #-}
  {-# Inline [0] termStreamIndex #-}