{-# LANGUAGE UnicodeSyntax, FlexibleContexts, FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses #-}

-- | Enquiry of the graph structure. Note: In this module the term "node" is often used synonymously to "node reference" and "node value". The two can easily distinguished by their type: the former has type 'Node' the latter usually 'n'.
module GraphRewriting.Graph.Read
	(module Data.View,
	 module GraphRewriting.Graph.Types,
	 module GraphRewriting.Graph.Read)
where

import Prelude.Unicode
import GraphRewriting.Graph.Types
import GraphRewriting.Graph.Internal
import Control.Monad.Reader
import qualified Data.IntMap as Map
import qualified Data.IntSet as Set
import Data.View
import Data.List (nub)


type WithGraph n = Reader (Graph n)

-- | This forces the use of the 'Reader' monad. Wrapping a sequence of monadic read-only operations (such as those defined below) into a read-only block can save much overhead e.g. in the state monad.
readOnly  MonadReader (Graph n) m  Reader (Graph n) a  m a
readOnly r = liftM (runReader r) ask

existNode  MonadReader (Graph n) m  Node  m Bool
existNode (Node n) = liftM (Map.member n) (asks nodeMap)

readNode  MonadReader (Graph n) m  Node  m n
readNode (Node n) = maybe (fail $ "readNode: node with ID "  show n  " does not exist") return . readRef n =<< asks nodeMap

-- | a wrapper to 'inspect' the given node
inspectNode  (View v n, MonadReader (Graph n) m)  Node  m v
inspectNode = liftM inspect . readNode

-- | a wrapper to 'examine' the given node
examineNode  (View v n, MonadReader (Graph n) m)  (v  a)  Node  m a
examineNode f = liftM (examine f) . readNode

-- | all of the graph's nodes
readNodeList  MonadReader (Graph n) m  m [Node]
readNodeList = liftM (map Node . Map.keys) (asks nodeMap)

-- | all of the graph's edges
readEdgeList  MonadReader (Graph n) m  m [Edge]
readEdgeList = liftM (map Edge . Map.keys) (asks edgeMap)

-- | edges attached to the given node
attachedEdges  (View [Port] n, MonadReader (Graph n) m)  Node  m [Edge]
attachedEdges = liftM nub . inspectNode

-- | non-empty set of nodes attached to the given edge
attachedNodes  MonadReader (Graph n) m  Edge  m [Node]
attachedNodes = liftM (map Node . Set.elems) . readEdge

-- | amount of ports the given hyperedge is connected to
edgeCardinality  (View [Port] n, MonadReader (Graph n) m)  Edge  m Int
edgeCardinality e = liftM (length . filter (e ) . concat) (mapM inspectNode =<< attachedNodes e)

-- | list of nodes that are connected to the given node, not including the node itself
neighbours  (View [Port] n, MonadReader (Graph n) m)  Node  m [Node]
neighbours n = do
	is  liftM Set.unions $ mapM readEdge =<< inspectNode n
	return $ map Node $ Set.elems $ Set.delete (nKey n) is

-- | list of nodes that are connected to the given node, including the node itself
relatives  (View [Port] n, MonadReader (Graph n) m)  Node  m [Node]
relatives n = do
	is  liftM Set.unions $ mapM readEdge =<< inspectNode n
	return $ map Node $ Set.elems is

-- | nodes connected to given port of the specified node, not including the node itself
adverseNodes  MonadReader (Graph n) m  Node  Port  m [Node]
adverseNodes (Node n) p = liftM (map Node . Set.elems . Set.delete n) (readEdge p)

-- | whether two nodes are connected
connected  (View [Port] n, MonadReader (Graph n) m)  Node  Node  m Bool
connected n1 n2 = liftM (n2 ) (relatives n2)

-- | whether the given ports features a dangling edge
dangling  (View [Port] n, MonadReader (Graph n) m)  Port  m Bool
dangling = liftM ( 1) . edgeCardinality

-- | Map node-relative enquiry over the nodes of the graph.
withNodes  MonadReader (Graph n) m  (Node  m a)  m [a]
withNodes p = mapM p =<< readNodeList