-- | Module that provides 'Graph' type class and several useful functions
-- for interaction with 'Graph's.
--
module Math.Grads.Graph
  ( EdgeList
  , Graph (..)
  , GraphEdge
  , changeIndsEdge
  , changeTypeEdge
  , edgeType
  ) where

import           Data.List (nub)

-- | 'GraphEdge' is just triple, containing index of starting vertex of edge,
-- index of ending vertex of edge and edge's type.
--
type GraphEdge e = (Int, Int, e)

-- | Type alias for list of 'GraphEdge's.
--
type EdgeList e = [GraphEdge e]

-- | Get edge's type from 'GraphEdge'.
--
edgeType :: GraphEdge e -> e
edgeType (_, _, t) = t

-- | Given transformation of edge types transforms 'GraphEdge'.
--
changeTypeEdge :: (e1 -> e2) -> GraphEdge e1 -> GraphEdge e2
changeTypeEdge f (a, b, t) = (a, b, f t)

-- | Given transformation of edge's indices transforms 'GraphEdge'.
--
changeIndsEdge :: (Int -> Int) -> GraphEdge e -> GraphEdge e
changeIndsEdge f (a, b, t) = (f a, f b, t)

-- | Type class that gives data structure properties of graph.
--
class Graph g where
  -- | Construct a graph from list of vertices and edges.
  --
  fromList :: (Ord v, Eq v) => ([v], [GraphEdge e]) -> g v e

  -- | Get a list of all vertices and edges from the graph.
  --
  toList :: (Ord v, Eq v) => g v e -> ([v], [GraphEdge e])

  -- | Get the number of vertices.
  --
  vCount :: g v e -> Int

  -- | Unsafe get adjacent vertices.
  --
  infixl 9 !>
  (!>) :: (Ord v, Eq v) => g v e -> v -> [(v, e)]

  -- | Unsafe get adjacent indices.
  --
  infixl 9 !.
  (!.) :: g v e -> Int -> [(Int, e)]

  -- | Safe get adjacent vertices.
  --
  infixl 9 ?>
  (?>) :: (Ord v, Eq v) => g v e -> v -> Maybe [(v, e)]

  -- | Safe get adjacent indices.
  --
  infixl 9 ?.
  (?.) :: g v e -> Int -> Maybe [(Int, e)]

  -- | Get a list of edges starting at given vertex.
  --
  incident :: (Ord v, Eq v) => g v e -> v -> [(v, v, e)]
  incident gr at = (\(a, b) -> (at, a, b)) <$> gr !> at

  -- | Safe get a list of edges starting at given vertex.
  --
  safeIncident :: (Ord v, Eq v) => g v e -> v -> Maybe [(v, v, e)]
  safeIncident gr at = map (\(a, b) -> (at, a, b)) <$> gr ?> at

  -- | Get a list of index edges starting at given vertex.
  --
  incidentIdx :: (Eq e) => g v e -> Int -> [GraphEdge e]
  incidentIdx gr idx = nub ((\(a, b) -> (min idx a, max idx a, b)) <$> gr !. idx)

  -- | Safe get a list of index edges starting at given vertex.
  --
  safeIncidentIdx :: (Eq e) => g v e -> Int -> Maybe [GraphEdge e]
  safeIncidentIdx gr idx = nub <$> (map (\(a, b) -> (min idx a, max idx a, b)) <$> gr ?. idx)