{-# LANGUAGE UnicodeSyntax, FlexibleContexts #-}
module GraphRewriting.Strategies.LeftmostOutermost where

import GraphRewriting.Pattern
import GraphRewriting.Graph.Write
import GraphRewriting.Graph.Read
import GraphRewriting.Rule
import GraphRewriting.Strategies.Control
import Data.List (intersect, (\\))


-- | Gives us the the 'left' port for a given node
class LeftmostOutermost n where lmoPort  n  Maybe Port

instance LeftmostOutermost n  LeftmostOutermost (Wrapper n) where lmoPort = lmoPort . wrapped

getLmoPort  (LeftmostOutermost n)  Node  Pattern n Port
getLmoPort n = do
	node  liftReader $ readNode n
	case lmoPort node of
		Nothing  fail "Term is in WHNF"
		Just lo  return lo

-- It does not compile with this type signature, even when IncoherentInstances are given in Control.
moveControl  (View [Port] n, View Control n, LeftmostOutermost n) => Rule n
moveControl = do
	Control {stack = s}  node
	control  previous
	lmo1  getLmoPort control
	n  branchNodes =<< liftReader . adverseNodes control =<< getLmoPort control
	return $ do
		updateNode control NoControl
		updateNode n (Control {stack = control : s})

-- It does not compile with this type signature, even when IncoherentInstances are given in Control.
leftmostOutermost  (View Control n, View [Port] n)  Rule n  Rule n
leftmostOutermost r = do
	rewrite  r
	ns  history -- we want the first node of the matching pattern
	let topnode = last ns
	Control {stack = s}  liftReader $ inspectNode topnode
	return $ do
		updateNode topnode NoControl -- First we set the topnode to not be the control node any more
		oldNodes  readNodeList
		rewrite -- then we perform the rewrite
		newNodes  readNodeList
		let s' = intersect s newNodes -- only consider nodes for the control marker that exist
		if null s' -- even the topmost node has been replaced
			then do -- we assign the control marker to one of the newly created nodes
				let addedNodes = newNodes \\ oldNodes
				updateNode (head addedNodes) (Control {stack = []}) -- finally we set the previous node on the stack as the control node
			else do -- set the previous node on the stack as the control node
				updateNode (head s') (Control {stack = tail s'})