module HasseDiagram
  ( Node
  , HasseDiagram
  , hsd2String
  , encodeDirectedGraph
  , directedFlagComplex
  , toSimplicialComplex
  ) where
import Util
import SimplicialComplex
import Data.List as L
import Data.Vector as V
type Node = (Vector Int, Vector Int, Vector Int)
type HasseDiagram = Vector (Vector Node)
hsd2String :: HasseDiagram -> String
hsd2String = (L.intercalate "\n\n") . V.toList . (V.map (L.intercalate "\n" . V.toList . V.map show))
encodeDirectedGraph :: Int -> [(Int, Int)] -> HasseDiagram
encodeDirectedGraph numVerts cxns =
  let verts       = V.map (\n -> (n `cons` V.empty, V.empty, V.empty)) $ 0 `range` (numVerts - 1)
      encodeEdges _ vertices edges []          = V.empty `snoc` vertices `snoc` edges
      encodeEdges n vertices edges ((i, j):xs) =
        let v1 = vertices ! i; v2 = vertices ! j; edge = V.empty `snoc` j `snoc` i
        in encodeEdges (n + 1)
          (replaceElem i (one v1, two v1, (thr v1) `snoc` n) $
            replaceElem j (one v2, two v2, (thr v2) `snoc` n) vertices) 
              (edges `snoc` (edge, edge, V.empty)) xs
  in encodeEdges 0 verts V.empty cxns
directedFlagComplex :: HasseDiagram -> HasseDiagram
directedFlagComplex directedGraph =
  let edges    = V.last directedGraph
      fstSinks =
        V.map (\e ->
          V.map (\(e0, _) -> (two e0) ! 0) $
            findBothElems (\e1 e2 -> (two e1) ! 0 == (two e2) ! 0)
              (V.filter (\e0 -> (two e0) ! 1 == (two e) ! 1) edges) (V.filter (\e0 -> (two e0) ! 1 == (two e) ! 0) edges)) edges
      
      makeLevel :: Bool -> HasseDiagram -> Vector Node -> Vector (Vector Int) -> (Vector Node, Vector Node, Vector (Vector Int))
      makeLevel fstIter result oldNodes oldSinks =
        let maxindex = V.length oldNodes
            
            makeNode :: Int -> Int -> Int -> Vector Node -> Vector Int -> (Vector Node, Node, Vector Int)
            makeNode newIndex oldIndex sinkIndex nodes sinks =
              let sink     = sinks ! sinkIndex
                  oldNode  = nodes ! oldIndex
                  verts    = sink `cons` (one oldNode) 
                  numFaces = V.length $ two oldNode
                  
                  testTargets :: Int -> Node -> Vector Node -> Node -> Vector Int -> (Vector Node, Node, Vector Int)
                  testTargets i onode onodes newNode newSinks =
                    let faceVerts =
                          if fstIter then one $ (V.last $ V.init $ result) ! ((two onode) ! i)
                          else one $ (V.last $ result) ! ((two onode) ! i)
                    in
                      if i == numFaces then (onodes, newNode, newSinks)
                      else
                        case V.find (\(_, (v, _, _)) -> V.head v == sink && V.tail v == faceVerts) $ mapWithIndex (\j n -> (j, n)) onodes of
                          Just (j, n) ->
                            testTargets (i + 1) onode
                              (replaceElem j (one n, two n, (thr n) `smartSnoc` newIndex) onodes)
                                (one newNode, (two newNode) `snoc` j, thr newNode) (newSinks |^| (oldSinks ! j))
                          Nothing     -> error "Face not found, HasseDiagram.directedFlagComplex.makeDiagram.makeNode.testTargets"
              in testTargets 0 oldNode nodes (verts, oldIndex `cons` V.empty, V.empty) sinks
            loopSinks :: Int -> Int -> Vector Node -> (Vector Node, Vector Node, Vector (Vector Int), Int)
            loopSinks nodeIndex lastIndex nodes =
              let node     = oldNodes ! nodeIndex
                  sinks    = oldSinks ! nodeIndex
                  numSinks = V.length sinks
                  loop i (modifiedNodes, newNodes, newSinks) =
                    if i == numSinks then (modifiedNodes, newNodes, newSinks, i + lastIndex)
                    else
                      let (modNodes, newNode, ns) = makeNode (i + lastIndex) nodeIndex i modifiedNodes sinks
                      in loop (i + 1) (modNodes, newNodes `snoc` newNode, newSinks `snoc` ns)
              in loop 0 (nodes, V.empty, V.empty)
            loopNodes :: Int -> Int -> Vector Node -> Vector Node -> Vector (Vector Int) -> (Vector Node, Vector Node, Vector (Vector Int))
            loopNodes i lastIndex nodes newNodes newSinks =
              if i == maxindex then (nodes, newNodes, newSinks)
              else
                let (modifiedNodes, nnodes, nsinks, index) = loopSinks i lastIndex nodes
                in loopNodes (i + 1) lastIndex modifiedNodes (newNodes V.++ nnodes) (newSinks V.++ nsinks)
        in loopNodes 0 0 oldNodes V.empty V.empty
      loopLevels :: Int -> HasseDiagram -> Vector Node -> Vector (Vector Int) -> HasseDiagram
      loopLevels iter diagram nextNodes sinks =
        let (modifiedNodes, newNodes, newSinks) = makeLevel (iter < 2) diagram nextNodes sinks
            newDiagram                          = diagram `snoc` modifiedNodes
        in
          if V.null newNodes then newDiagram
          else loopLevels (iter + 1) newDiagram newNodes newSinks
  in loopLevels 0 directedGraph edges fstSinks
toSimplicialComplex :: HasseDiagram -> SimplicialComplex
toSimplicialComplex diagram =
  let sc = V.map (V.map not3) $ V.tail diagram
  in (V.length $ V.head diagram, (V.map (\(v, _) -> (v, V.empty)) $ sc ! 0) `cons` V.tail sc)