module DDC.Core.Flow.Context.FillPath
        ( FillMap, FillPath
        , pathsOfFills
        , getAccForPath
        , getAcc
        , isSimple
        , isNone )
where
import DDC.Type.Exp
import DDC.Core.Flow.Prim
import DDC.Core.Flow.Process.Operator
import DDC.Core.Flow.Context.Base

import qualified Data.Map as Map
import Control.Monad


type FillMap = Map.Map Name (FillPath, Type Name)

data FillPath
        = PathNone
        | PathRate      (Type  Name)
        | PathSelect    (Bound Name)
        | PathSegment   (Bound Name)
        | PathAppend     FillPath       FillPath
        deriving (Eq, Show)


pathsOfFills :: Context -> Maybe FillMap
pathsOfFills ctx
 = go ctx Map.empty
 where
  go c@ContextAppend{} _
   = do m1 <- go (contextInner1 c) Map.empty
        m2 <- go (contextInner2 c) Map.empty
        return $ Map.unionWith merge
          (Map.map appl m1)
          (Map.map appr m2)

  go c m
   = do m' <- insertFillsNoDupes (contextOps c) (path c) m
        foldM (flip go) m' (contextInner c)

  appl (p,t)
   = (PathAppend p PathNone, t)
  appr (p,t)
   = (PathAppend PathNone p, t)

  merge (PathAppend PathNone _, t) (PathAppend _ PathNone, _)
   = (PathNone, t)
  merge (PathAppend l _, t) (PathAppend _ r, _)
   = (PathAppend l r, t)
  merge _ _
   = error "ddc-core-flow.pathsOfFills: impossible!"

  path c@ContextRate{} 
   = PathRate $ contextRate c
  path c@ContextSelect{} 
   = PathSelect $ contextFlags c
  path c@ContextSegment{} 
   = PathSegment $ contextLens c
  path ContextAppend{} 
   = PathAppend PathNone PathNone


  insertFillsNoDupes ops p m
   = foldM (insert1 p) m ops

  insert1 p m OpFill{ opTargetVector = UName n
                    , opElemType     = ty }
   = case Map.lookup n m of
     Nothing
      -> Just (Map.insert n (p,ty) m)
     Just _
      -> Nothing
  insert1 _ m _
   = Just m


isPrefixOf :: FillPath -> FillPath -> Bool
isPrefixOf PathNone _
 = True
isPrefixOf (PathAppend h i) (PathAppend j k)
 =  h == j && isPrefixOf i k
 || i == PathNone && k == PathNone && isPrefixOf h j
isPrefixOf a b
 = a == b

isNone :: FillPath -> Bool
isNone PathNone
 = True
isNone (PathAppend i j)
 = isNone i && isNone j
isNone _
 = False

-- A simple fill path has only one place it's filling, and it's just a rate with no select or segment
isSimple :: FillPath -> Bool
isSimple (PathAppend i j)
 =  isSimple i && isNone j
 || isSimple j && isNone i
isSimple (PathRate _)
 = True
isSimple _
 = False



getAccForPath :: FillMap -> FillPath -> Maybe Name
getAccForPath m fp
 = case Map.minViewWithKey $ Map.filter search m of
   Nothing            -> Nothing
   Just ((k,_),_)     -> Just k
 where
  search (fp', _)
   = isPrefixOf fp fp'

-- If acc is Nothing, you can just use the current index ^0
getAcc :: FillMap -> Name -> Maybe Name
getAcc m n
 = case Map.lookup n m of
   Nothing
    -> Nothing -- ???
   Just (fp, _)
    -> if   isSimple fp
       then Nothing
       else getAccForPath m fp

-- I don't think this actually gives us the fewest number of accumulators.
-- Depending on the ordering of the map, maybe we'd have
--
-- [ h = App a None;
--   i = App a None;
--   j = App a b;
--   k = App a c ]
--   
-- here, @h@ and @i@ will be given the same accumulator, but @j@ and @k@ need separate
-- accumulators: three accumulators in total.
--
-- If the order were different, such as
-- [ j = App a b;
--   h = App a None;
--   i = App a None;
--   k = App a c ]
-- then searching for @h@ or @i@ would find @j@, and we would end up with only two accumulators, @j@ and @k@.
--