module DDC.Core.Flow.Transform.Extract.Intersperse
        (intersperseStmts)
where
import DDC.Core.Flow.Compounds
import DDC.Core.Flow.Prim
import DDC.Core.Flow.Exp
import DDC.Core.Collect
import DDC.Core.Transform.Annotate
import DDC.Type.Env
import Data.List (partition, (\\))
import qualified Data.Set as Set


-- | Given two lists of lets, order them so that any variables are bound before use.
intersperseStmts :: [LetsF] -> [LetsF] -> [LetsF]
intersperseStmts ls rs
 = let bls = nubbish $ map takeSubstBoundsOfBinds $ map valwitBindsOfLets ls
       brs = nubbish $ map takeSubstBoundsOfBinds $ map valwitBindsOfLets rs
   in  intersperse' (ls `zip` bls ++ rs `zip` brs)


-- Because a name might be bound a couple of times 
-- (see extractStmtEnd:EndVecSlice)
nubbish :: [[Bound Name]] -> [[Bound Name]]
nubbish bs' = go bs' []
 where  go [] _        = []
        go (b:bs) accs = (b \\ accs) : go bs (accs ++ b)


intersperse' 
        :: [(Lets () Name, [Bound Name])]
        -> [Lets () Name]

intersperse' [] = []

intersperse' ((x,b):bxs)
 -- Check if any of the free variables in x are bound later on.
 -- If so, defer this binding...
 | f            <- freeXLets x
 , (r:rs,os)    <- partition (any (flip Set.member f) . snd) bxs
 , (x', _)     <- r
 = x' : intersperse' (rs ++ (x, b) : os)

 -- Otherwise it's a valid binding
 | otherwise
 = x : intersperse' bxs


freeXLets :: LetsF -> Set.Set (Bound Name)
freeXLets ll
 = freeX empty $ annotate () (XLet ll (XCon (dcBool True)))