module Sifflet.Data.TreeGraph 
    (LayoutGraph, 
     flayoutToGraph, treeLayoutToGraph,
     
     orderedTreeToGraph, 
     treeGraphNodesTree, graphToTreeOriginal,
     graphToTreeStructure, 
     flayoutToGraphRoots,
     graphToOrderedTree, graphToOrderedTreeFrom,
     orderedChildren, adjCompareEdge,
     nextNodes, 
     grTranslateNode, grTranslateSubtree, grTranslateGraph,
     
     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 :: 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 []
sprout :: G.Node -> Tree e -> [(G.Node, WEdge, Tree e)]
sprout parent (T.Node _ subtrees) =
    
    let m = length subtrees  1
    in [(parent, WEdge e, s) | (e, s) <- zip [0..m] subtrees]
orderedTreeToGraph :: Tree e -> Gr e WEdge
orderedTreeToGraph otree  = 
    
    
    
    
    
    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 g [] _ = g
        grow g ((p, e, t):pets) n =
            
            
            let adj = (e, p) 
                g' = ([adj], n, (rootLabel t), []) & g 
                n' = succ n
            in grow g' (pets ++ 
                        sprout n t
                        
                        )
                       n'
    in grow g1 (sprout 1 otree) 2
treeGraphNodesTree :: Tree e -> Tree Node
treeGraphNodesTree atree =
    
    let gnTree :: Tree e -> Node -> Node -> (Tree Node, Node)
        gnTree (T.Node _root subtrees) rootNode next =
            
            
            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 = 
  
  
  
  
  
  let n = length items
      next' = next + n
  in ([next .. (next'  1)], next')
graphToOrderedTree :: Gr e WEdge -> Tree e
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]
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
graphToTreeOriginal :: Gr e () -> G.Node -> Tree e
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]
graphToTreeStructure :: Gr n e -> G.Node -> Tree G.Node
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') ->
        
        (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 
            
            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)