module ProjectM36.GraphRefRelationalExpr where
--evaluate relational expressions across the entire transaction graph to support cross-transaction referencing
import ProjectM36.Base
import qualified Data.Set as S

--import Debug.Trace

data SingularTransactionRef = SingularTransactionRef GraphRefTransactionMarker |
                              MultipleTransactionsRef |
                              NoTransactionsRef
                              deriving (Eq, Show)

instance Semigroup SingularTransactionRef where
  NoTransactionsRef <> x = x
  MultipleTransactionsRef <> _ = MultipleTransactionsRef
  SingularTransactionRef tidA <> s@(SingularTransactionRef tidB) =
    if tidA == tidB then
      s
    else
      MultipleTransactionsRef
  s@SingularTransactionRef{} <> NoTransactionsRef = s
  _ <> MultipleTransactionsRef = MultipleTransactionsRef

instance Monoid SingularTransactionRef where
  mempty = NoTransactionsRef

-- | return `Just transid` if this GraphRefRelationalExpr refers to just one transaction in the graph. This is useful for determining if certain optimizations can apply.
singularTransaction :: Foldable t => t GraphRefTransactionMarker -> SingularTransactionRef
singularTransaction expr
  | S.null transSet = NoTransactionsRef
  | S.size transSet == 1 = SingularTransactionRef (head (S.toList transSet))
  | otherwise = MultipleTransactionsRef
  where
    transSet = foldr S.insert S.empty expr

-- | Return True if two 'GraphRefRelationalExpr's both refer exclusively to the same transaction (or none at all).
inSameTransaction :: GraphRefRelationalExpr -> GraphRefRelationalExpr -> Maybe GraphRefTransactionMarker
inSameTransaction exprA exprB = case (stA, stB) of
  (SingularTransactionRef tA, SingularTransactionRef tB) | tA == tB -> Just tA
  _ -> Nothing
  where stA = singularTransaction exprA
        stB = singularTransaction exprB

singularTransactions :: (Foldable f, Foldable t) => f (t GraphRefTransactionMarker) -> SingularTransactionRef
singularTransactions = foldMap singularTransaction