-- (c) 2000-2005 by Martin Erwig [see file COPYRIGHT]
-- | Graph Voronoi Diagram
--
--   These functions can be used to create a /shortest path forest/
--   where the roots are specified.
module Data.Graph.Inductive.Query.GVD (
Voronoi,LRTree,
gvdIn,gvdOut,
voronoiSet,nearestNode,nearestDist,nearestPath,
--    vd,nn,ns,
--    vdO,nnO,nsO
) where

import Data.List  (nub)
import Data.Maybe (listToMaybe)

import qualified Data.Graph.Inductive.Internal.Heap as H

import Data.Graph.Inductive.Basic
import Data.Graph.Inductive.Graph
import Data.Graph.Inductive.Internal.RootPath
import Data.Graph.Inductive.Query.SP          (dijkstra)

-- | Representation of a shortest path forest.
type Voronoi a = LRTree a

-- | Produce a shortest path forest (the roots of which are those
--   nodes specified) from nodes in the graph /to/ one of the root
--   nodes (if possible).
gvdIn :: (DynGraph gr, Real b) => [Node] -> gr a b -> Voronoi b
gvdIn vs g = gvdOut vs (grev g)

-- | Produce a shortest path forest (the roots of which are those
--   nodes specified) from nodes in the graph /from/ one of the root
--   nodes (if possible).
gvdOut :: (Graph gr, Real b) => [Node] -> gr a b -> Voronoi b
gvdOut vs = dijkstra (H.build (zip (repeat 0) (map (\v->LP [(v,0)]) vs)))

-- | Return the nodes reachable to/from (depending on how the
--   'Voronoi' was constructed) from the specified root node (if the
--   specified node is not one of the root nodes of the shortest path
--   forest, an empty list will be returned).
voronoiSet :: Node -> Voronoi b -> [Node]
voronoiSet v = nub . concat . filter (\p->last p==v) . map (map fst . unLPath)

-- | Try to construct a path to/from a specified node to one of the
--   root nodes of the shortest path forest.
maybePath :: Node -> Voronoi b -> Maybe (LPath b)
maybePath v = listToMaybe . filter ((v==) . fst . head . unLPath)

-- | Try to determine the nearest root node to the one specified in the
--   shortest path forest.
nearestNode :: Node -> Voronoi b -> Maybe Node
nearestNode v = fmap (fst . last . unLPath) . maybePath v

-- | The distance to the 'nearestNode' (if there is one) in the
--   shortest path forest.
nearestDist :: Node -> Voronoi b -> Maybe b
nearestDist v = fmap (snd . head . unLPath) . maybePath v

-- | Try to construct a path to/from a specified node to one of the
--   root nodes of the shortest path forest.
nearestPath :: Node -> Voronoi b -> Maybe Path
nearestPath v = fmap (map fst . unLPath) . maybePath v

-- vd = gvdIn [4,5] vor
-- vdO = gvdOut [4,5] vor
-- nn = map (flip nearestNode vd) [1..8]
-- nnO = map (flip nearestNode vdO) [1..8]
-- ns = map (flip voronoiSet vd) [1..8]
-- nsO = map (flip voronoiSet vdO) [1..8]