module Sifflet.Data.TreeGraph 
    (LayoutGraph, 
     flayoutToGraph, treeLayoutToGraph,
     -- treeToGraph, 
     orderedTreeToGraph, 
     treeGraphNodesTree, graphToTreeOriginal,
     graphToTreeStructure, 
     flayoutToGraphRoots,
     graphToOrderedTree, graphToOrderedTreeFrom,
     orderedChildren, adjCompareEdge,
     nextNodes, -- exported for testing; any other reason?
     grTranslateNode, grTranslateSubtree, grTranslateGraph,

     -- moved from Workspace.hs:
     functoidToFunction, graphToExprTree,

    )

where

import Data.List (sort, sortBy)

import Data.Graph.Inductive as G

import Sifflet.Data.Functoid
import Sifflet.Data.Geometry
import Sifflet.Data.Tree as T
import Sifflet.Data.TreeLayout
import Sifflet.Data.WGraph
import Sifflet.Language.Expr
import Sifflet.Util

type LayoutGraph n e = Gr (LayoutNode n) e

flayoutToGraph :: FunctoidLayout -> WGraph
flayoutToGraph tlo = 
    case tlo of 
      FLayoutTree t -> treeLayoutToGraph t
      FLayoutForest ts _bbox -> 
          foldl grAddGraph wgraphNew (map treeLayoutToGraph ts)

treeLayoutToGraph :: TreeLayout ExprNode -> WGraph
treeLayoutToGraph = orderedTreeToGraph . fmap WSimple


-- flayoutToGraphRoots returns a list of graph nodes (Ints)
-- corresponding to the root of the tree, or the roots of
-- the trees in the forest.
-- The list is ordered in the same sense as the graph nodes.

flayoutToGraphRoots :: FunctoidLayout -> [G.Node]
flayoutToGraphRoots (FLayoutTree _t) = [1]
flayoutToGraphRoots (FLayoutForest trees _bbox) =
    let loop _ [] res = reverse res
        loop next (t:ts) res =
            loop (next + treeSize t) ts (next:res)
    in loop 1 trees []


-- {-# DEPRECATED treeToGraph "use ??? instead" #-}

-- treeToGraph :: Tree e -> Gr e ()
-- treeToGraph (T.Node root subtrees)  = 
--     -- Convert an ordered tree to a graph.
--     -- The graph edge labels from parent to child 
--     -- are integers corresponding to the order of the children.
--     -- So, to recover the ordered list of children, 
--     -- sort by the edge label.
--     let g0 = empty :: Gr e ()
--         g1 = insNode (1, root) g0

--         grow :: Gr e () -> [(Tree e, G.Node)] -> G.Node -> Gr e ()
--         -- grow graph [(tree, parent)] node
--         grow g [] _ = g
--         grow g ((t, p):tps) n =
--             -- insert t forming g', insert ts into g'
--             let edge = ((), p) -- predecessors, or both?
--                 g' = ([edge], n, (rootLabel t), []) & g -- assume pred only
--             in grow g' (tps ++ [(s, n) | s <- subForest t])
--                        (succ n)

--     in grow g1 [(s, 1) | s <- subtrees] 2

sprout :: G.Node -> Tree e -> [(G.Node, WEdge, Tree e)]
sprout parent (T.Node _ subtrees) =
    -- create triples (parent graph node, edge, subtree)
    let m = length subtrees - 1
    in [(parent, WEdge e, s) | (e, s) <- zip [0..m] subtrees]


orderedTreeToGraph :: Tree e -> Gr e WEdge
orderedTreeToGraph otree  = 
    -- Convert an ordered tree to a graph.
    -- The graph edge labels from parent to child 
    -- are integers corresponding to the order of the children.
    -- So, to recover the ordered list of children, 
    -- sort by the edge label.
    let g0 = empty :: Gr e WEdge
        g1 = insNode (1, rootLabel otree) g0

        grow :: Gr e WEdge -> [(G.Node, WEdge, Tree e)] -> G.Node -> Gr e WEdge
        -- grow graph [(parent, edgeLabel, subtree), ...] node
        grow g [] _ = g
        grow g ((p, e, t):pets) n =
            -- insert pet forming g', insert pets into g';
            -- n is the node id for the root of this subtree
            let adj = (e, p) -- to parent (priority, node)
                g' = ([adj], n, (rootLabel t), []) & g 
                n' = succ n
            in grow g' (pets ++ 
                        sprout n t
                        -- [(s, n) | s <- subForest t])
                        )
                       n'

    in grow g1 (sprout 1 otree) 2

-- And what about this (e, Node) type?  Is it some sort of monad?

treeGraphNodesTree :: Tree e -> Tree Node
treeGraphNodesTree atree =
    -- returns a tree of Nodes of the Graph (treeToGraph atree)
    let gnTree :: Tree e -> Node -> Node -> (Tree Node, Node)
        gnTree (T.Node _root subtrees) rootNode next =
            -- rootNode = Node (number) for the root,
            -- next = next unused Node
            let (nNodes, next') = nextNodes subtrees next
                (subtrees', next'') = gnSubtrees subtrees nNodes next'
            in (T.Node rootNode subtrees', next'')

        gnSubtrees :: [Tree e] -> [Node] -> Node -> ([Tree Node], Node)
        gnSubtrees [] [] next = ([], next)
        gnSubtrees (t:ts) (n:ns) next = 
            let (t', next') = gnTree t n next
                (ts', next'') = gnSubtrees ts ns next'
            in ((t' : ts'), next'')
        gnSubtrees _ _ _ = error "gnSubtrees: list lengths do not match"

    in fst (gnTree atree 1 2)

nextNodes :: [e] -> Node -> ([Node], Node)
nextNodes items next = 
  -- next is the next unused Node (number).
  -- Returns list of new nodes, and a new "next" node
  -- E.g., nextNodes [a, b] 3 = ([3, 4], 5)
  --       nextNodes [c, d, e] 5 = ([5, 6, 7], 8)
  --       nextNodes [] 8 = ([], 8)
  let n = length items
      next' = next + n
  in ([next .. (next' - 1)], next')

-- When a tree is converted to a graph,
-- each tree node's ordered children get graph node numbers
-- in ascending order.  Therefore, when reconstructing the tree,
-- sorting the graph nodes restores the order of the children
-- as in the tree.

graphToOrderedTree :: Gr e WEdge -> Tree e
-- inverse of orderedTreeToGraph
graphToOrderedTree g = graphToOrderedTreeFrom g 1

graphToOrderedTreeFrom :: Gr e WEdge -> G.Node -> Tree e
graphToOrderedTreeFrom g n =
    case lab g n of
      Just label -> 
          T.Node label (map (graphToOrderedTreeFrom g) (orderedChildren g n))
      Nothing -> 
          errcats ["missing label for node", show n]

-- | List of the nodes children, ordered by edge number
orderedChildren :: Gr e WEdge -> G.Node -> [G.Node]
orderedChildren g = map fst . sortBy adjCompareEdge . lsuc g

adjCompareEdge :: (Node, WEdge) -> (Node, WEdge) -> Ordering
adjCompareEdge (_n1, e1) (_n2, e2) = compare e1 e2

{-# DEPRECATED graphToTreeOriginal "use ??? instead" #-}

graphToTreeOriginal :: Gr e () -> G.Node -> Tree e
-- (\g -> graphToTreeOriginal g 1) is the inverse of treeToGraph
graphToTreeOriginal g n =
    case lab g n of
      Just label -> T.Node label (map (graphToTreeOriginal g) 
                                      (sort (suc g n)))
      _ -> errcats ["missing label for node", show n]

{-# DEPRECATED graphToTreeStructure "use ??? instead" #-}

graphToTreeStructure :: Gr n e -> G.Node -> Tree G.Node
-- This is *not* an inverse of treeToGraph.
-- Rather, graphToTreeStructure (treeToGraph t 1) is a tree t' of Nodes
-- (i.e., integer identifiers of nodes in the graph)
-- which parallels the structure of t.

graphToTreeStructure g n = T.Node n (map (graphToTreeStructure g)
                                         (sort (suc g n)))

grTranslateNode :: 
  Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
grTranslateNode node dx dy graph = 
    grUpdateNodeLabel graph node (translate dx dy)

grTranslateSubtree :: 
  Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
grTranslateSubtree root dx dy graph = 
    let trSubtrees :: [Node] -> LayoutGraph n e -> LayoutGraph n e
        trSubtrees [] g = g
        trSubtrees (r:rs) g = trSubtrees (rs ++ suc g r)
                              (grTranslateNode r dx dy g)
    in trSubtrees [root] graph

grTranslateGraph :: Double -> Double -> LayoutGraph n e -> LayoutGraph n e
grTranslateGraph dx dy graph = nmap (translate dx dy) graph

grUpdateNodeLabel :: (DynGraph g) => g a b -> Node -> (a -> a) -> g a b
grUpdateNodeLabel graph node updater =
  case match node graph of
    (Nothing, _) -> error "no such node"
    (Just (preds, jnode, label, succs), graph') ->
        -- jnode == node
        (preds, jnode, updater label, succs) & graph'

functoidToFunction :: 
    Functoid -> WGraph -> G.Node -> Env -> SuccFail Function
functoidToFunction functoid graph frameNode env =
    case functoid of
      FunctoidFunc f -> Succ f
      FunctoidParts {fpName = name, fpArgs = args} ->
          let roots = suc graph frameNode
              expr = treeToExpr $ graphToExprTree graph (head roots)
              impl = Compound args expr
          in 
            -- Check whether the frame contains a single tree
            if length roots /= 1
            then Fail "The graph structure is not a tree!"
            else case decideTypes expr args env of
                   Left errmsg -> Fail errmsg
                   Right (atypes, rtype) -> 
                       Succ (Function (Just name) atypes rtype impl)


graphToExprTree :: WGraph -> G.Node -> Tree ExprNode
graphToExprTree g root =
    let extractExprNode wnode =
            case wnode of
              WSimple layoutNode -> gnodeValue (nodeGNode layoutNode)
              WFrame _ -> error "graphToExprTreeFrom: unexpected WFrame node"
    in fmap extractExprNode (graphToOrderedTreeFrom g root)