{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveDataTypeable #-}

-- | Graph defines the Graph class, which defines the basic things a
-- graph must do.  Peculiarities:
-- (1) Graphs are directed with labelled nodes and
--     arcs.  These nodes and arcs have types.
-- (2) The nodes and arcs are identified by values of type Node and Arc.
--     These values are essentially strings.  The strings are provided by
--     the user; there is no mechanism for generating new unique strings.
--     (This is because this is easy in the applications I have in mind.)
-- (3) A necessary feature of these graphs is that it is supposed to
--     be easy generate copies, both on the same system and on others.
module Graphs.Graph(
   Graph(..), -- the Graph class
   -- Instances are parameterised on
   -- nodeLabel, nodeTypeLabel, arcLabel, arcTypeLabel.

   -- Nodes, Arc, NodeTypes, Arc
   Node, Arc, NodeType, ArcType,
   -- These are all instances of AtomString.StringClass (and so Read & Show).
   -- This means that they are essentially strings; the different types
   -- are just there to add a little abstraction.
   -- They are also all instances of Eq and Ord.  However there
   -- is no guarantee that the ordering will be the same as for the
   -- corresponding strings.
   firstNode,
   -- :: Node
   -- first Node in the node ordering.

   -- They are also instances of Typeable.

   -- Updates
   Update(..),
   -- datatype encoding update to shared graph
   -- Like instances of Graph, parameterised on
   -- nodeLabel, nodeTypeLabel, arcLabel, arcTypeLabel.
   -- Derives Read and Show.

   CannedGraph(..),
   -- contains complete immutable contents of a Graph at some time
   -- Like instances of Graph, parameterised on
   -- nodeLabel, nodeTypeLabel, arcLabel, arcTypeLabel.
   -- Derives Read and Show.

   GraphConnection,
   GraphConnectionData(..),
   -- A GraphConnection contains the information generated by one
   -- instance of Graph, which can be used to construct another,
   -- including a CannedGraph.
   -- Like instances of Graph, parameterised on
   -- nodeLabel, nodeTypeLabel, arcLabel, arcTypeLabel.

   PartialShow(..),
      -- newtype alias for showing updates.
      -- NB.  This type might get moved into ExtendedPrelude if it proves
      -- useful elsewhere.

   ) where

import Util.AtomString
import Util.QuickReadShow
import Util.Dynamics
import Graphs.NewNames

class Graph graph where
   -- access functions
   getNodes :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO [Node]
   getArcs :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO [Arc]
   getNodeTypes :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO [NodeType]
   getArcTypes :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO [ArcType]

   getArcsOut :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Node -> IO [Arc]
   getArcsIn :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Node -> IO [Arc]
   getNodeLabel :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Node -> IO nodeLabel
   getNodeType :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Node -> IO NodeType
   getNodeTypeLabel :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> NodeType -> IO nodeTypeLabel

   getSource :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Arc -> IO Node
   getTarget :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Arc -> IO Node
   getArcLabel :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Arc -> IO arcLabel
   getArcType :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Arc -> IO ArcType
   getArcTypeLabel :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> ArcType -> IO arcTypeLabel

   shareGraph :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel
   newGraph :: GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO (graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel)

   -- Functions for changing the state.
   newNodeType :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> nodeTypeLabel -> IO NodeType
   newNode :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> NodeType -> nodeLabel -> IO Node
   newArcType :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> arcTypeLabel -> IO ArcType
   newArc :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> ArcType -> arcLabel -> Node -> Node -> IO Arc

   -- Other updates, such as deletions should be done with the update
   -- function.  It is also possible to add nodes, arcs, arctypes and
   -- nodetypes using update; however in this case the caller is responsible
   -- for providing a globally new label.
   update :: graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel -> IO ()

   newEmptyGraph :: IO (graph nodeLabel nodeTypeLabel arcLabel arcTypeLabel)
   -- Actually newEmptyGraph can be synthesised from the above functions
   -- by synthesising a null GraphConnection and passing it to newGraph.

------------------------------------------------------------------------
-- GraphConnection
------------------------------------------------------------------------

type GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel =
   (Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel -> IO ())
   -> IO (GraphConnectionData nodeLabel nodeTypeLabel arcLabel arcTypeLabel)
   -- The first argument is passed back to the parent graph and
   -- indicates where to put changes to the parent graph since the
   -- canned graph was made.

data GraphConnectionData nodeLabel nodeTypeLabel arcLabel arcTypeLabel =
      GraphConnectionData {
   graphState :: CannedGraph nodeLabel nodeTypeLabel arcLabel arcTypeLabel,
      -- current state of graph
   deRegister :: IO (),
      -- disables graphUpdates
   graphUpdate :: Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel
      -> IO(),
      -- Similar to update (in class definition) except that
      -- it doesn't get echoed on graphUpdates.
   nameSourceBranch :: NameSourceBranch
      -- A source of new names.  Each graph should contain a NameSource
      -- to generate new node strings.
   }

------------------------------------------------------------------------
-- Nodes, Arcs, NodeTypes, ArcTypes.
------------------------------------------------------------------------

newtype Node = Node AtomString deriving (Eq,Ord,Typeable)

instance StringClass Node where
   toString (Node atomString) = toString atomString
   fromString atomString = Node (fromString atomString)

instance Show Node where
   showsPrec = qShow

instance Read Node where
   readsPrec = qRead

firstNode :: Node
firstNode = Node firstAtomString

newtype NodeType = NodeType AtomString deriving (Eq,Ord,Typeable)

instance StringClass NodeType where
   toString (NodeType atomString) = toString atomString
   fromString atomString = NodeType (fromString atomString)

instance Show NodeType where
   showsPrec = qShow

instance Read NodeType where
   readsPrec = qRead

newtype Arc = Arc AtomString deriving (Eq,Ord,Typeable)

instance StringClass Arc where
   toString (Arc atomString) = toString atomString
   fromString atomString = Arc (fromString atomString)

instance Show Arc where
   showsPrec = qShow

instance Read Arc where
   readsPrec = qRead

newtype ArcType = ArcType AtomString deriving (Eq,Ord,Typeable)

instance StringClass ArcType where
   toString (ArcType atomString) = toString atomString
   fromString atomString = ArcType (fromString atomString)

instance Show ArcType where
   showsPrec = qShow

instance Read ArcType where
   readsPrec = qRead

------------------------------------------------------------------------
-- Update
------------------------------------------------------------------------

data Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel =
   -- NB.  For various reasons, we decree that DeleteNode and DeleteArc should
   -- return normally, doing nothing, should the node already be deleted.
      NewNodeType NodeType nodeTypeLabel
   |  SetNodeTypeLabel NodeType nodeTypeLabel
   |  NewNode Node NodeType nodeLabel
   |  DeleteNode Node
   |  SetNodeLabel Node nodeLabel
   |  SetNodeType Node NodeType
   |  NewArcType ArcType arcTypeLabel
   |  SetArcTypeLabel ArcType arcTypeLabel
   |  NewArc Arc ArcType arcLabel Node Node
   |  DeleteArc Arc
   |  SetArcLabel Arc arcLabel
   |  SetArcType Arc ArcType
   |  MultiUpdate [Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel]
      -- can be used to present unnecessary redrawing when making big
      -- updates.
   deriving (Read,Show)

-- ---------------------------------------------------------------------
-- Show instance which does not require argument types to be showable
-- ---------------------------------------------------------------------

newtype PartialShow a = PartialShow a

instance Show (PartialShow a) => Show (PartialShow [a]) where
   show (PartialShow as) = show (map PartialShow as)

instance Show (PartialShow (
      Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel)) where
   show (PartialShow update) = case update of
      NewNodeType nodeType nodeTypeLabel ->
         "NewNodeType " ++ show nodeType
      SetNodeTypeLabel nodeType nodeTypeLabel ->
         "SetNodeTypeLabel " ++ show nodeType
      NewNode node nodeType nodeLabel ->
         "NewNode " ++ show node ++ "::" ++ show nodeType
      DeleteNode node ->
         "DeleteNode " ++ show node
      SetNodeLabel node nodeLabel ->
         "SetNodeLabel " ++ show node
      SetNodeType node nodeType ->
         "SetNodeType " ++ show node ++ "::" ++ show nodeType
      NewArcType arcType arcTypeLabel ->
         "NewArcType " ++ show arcType
      SetArcTypeLabel arcType arcTypeLabel ->
         "SetArcTypeLabel " ++ show arcType
      NewArc arc arcType arcLabel node1 node2 ->
         "NewArc " ++ show arc ++ "::" ++ show arcType ++ " " ++ show node1
             ++ "->" ++ show node2
      DeleteArc arc ->
         "DeleteArc " ++ show arc
      SetArcLabel arc arcLabel ->
         "SetArcLabel " ++ show arc
      SetArcType arc arcType ->
         "SetArcType " ++ show arc ++ "::" ++ show arcType
      MultiUpdate updates -> "MultiUpdate " ++ show (PartialShow updates)

instance Show (PartialShow (
      CannedGraph nodeLabel nodeTypeLabel arcLabel arcTypeLabel)) where
   show (PartialShow (CannedGraph {updates = updates})) =
      "CannedGraph " ++ show (PartialShow updates)


------------------------------------------------------------------------
-- CannedGraph
------------------------------------------------------------------------

data CannedGraph nodeLabel nodeTypeLabel arcLabel arcTypeLabel =
   CannedGraph {
      updates :: [Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel]
      -- This list may only contain NewNodeType, NewNode, NewArcType and
      -- NewArc definitions.  The updates are processed in list order, so
      -- for example the endpoints of an Arc should be created before the Arc.
      } deriving (Read,Show)