package org.hanuna.gitalk.graphmodel.fragment;

import org.hanuna.gitalk.common.Get;
import org.hanuna.gitalk.graph.Graph;
import org.hanuna.gitalk.graph.elements.Edge;
import org.hanuna.gitalk.graph.elements.Node;
import org.hanuna.gitalk.graphmodel.GraphFragment;
import org.hanuna.gitalk.graphmodel.fragment.elements.SimpleGraphFragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashSet;
import java.util.Set;

/**
 * @author erokhins
 */
public class ShortFragmentGenerator {
    private final Graph graph;
    private Get<Node, Boolean> unhiddenNodes = new Get<Node, Boolean>() {
        @NotNull
        @Override
        public Boolean get(@NotNull Node key) {
            return false;
        }
    };

    public ShortFragmentGenerator(Graph graph) {
        this.graph = graph;
    }

    public void setUnhiddenNodes(Get<Node, Boolean> unhiddenNodes) {
        this.unhiddenNodes = unhiddenNodes;
    }

    private void addDownNodeToSet(@NotNull Set<Node> nodes, @NotNull Node node) {
        for (Edge edge : node.getDownEdges()) {
            Node downNode = edge.getDownNode();
            nodes.add(downNode);
        }
    }

    private boolean allUpNodeHere(@NotNull Set<Node> here, @NotNull Node node) {
        for (Edge upEdge : node.getUpEdges()) {
            if (!here.contains(upEdge.getUpNode())) {
                return false;
            }
        }
        return true;
    }

    @Nullable
    public GraphFragment getDownShortFragment(@NotNull Node startNode) {
        if (startNode.getType() == Node.Type.EDGE_NODE) {
            throw new IllegalArgumentException("small fragment may start only with COMMIT_NODE, but this node is: " + startNode);
        }

        Set<Node> upNodes = new HashSet<Node>();
        upNodes.add(startNode);
        Set<Node> notAddedNodes = new HashSet<Node>();
        addDownNodeToSet(notAddedNodes, startNode);

        Node endNode = null;

        int startRowIndex = startNode.getRowIndex() + 1;
        int lastIndex = graph.getNodeRows().size() - 1;

        boolean isEnd = false;
        for (int currentRowIndex = startRowIndex; currentRowIndex <= lastIndex && !isEnd; currentRowIndex++) {
            for (Node node : graph.getNodeRows().get(currentRowIndex).getNodes()) {
                if (notAddedNodes.remove(node)) {
                    if (notAddedNodes.isEmpty() && node.getType() == Node.Type.COMMIT_NODE) {
                        if (allUpNodeHere(upNodes, node)) { // i.e. we found smallFragment
                            endNode = node;
                        }
                        isEnd = true;
                        break;
                    } else {
                        if (!allUpNodeHere(upNodes, node) || unhiddenNodes.get(node)) {
                            isEnd = true;
                        }
                        upNodes.add(node);
                        addDownNodeToSet(notAddedNodes, node);
                    }
                }
            }
        }
        if (endNode == null) {
            return null;
        } else {
            upNodes.remove(startNode);
            return new SimpleGraphFragment(startNode, endNode, upNodes);
        }
    }


    private void addUpNodeToSet(@NotNull Set<Node> nodes, @NotNull Node node) {
        for (Edge edge : node.getUpEdges()) {
            Node upNode = edge.getUpNode();
            nodes.add(upNode);
        }
    }

    private boolean allDownNodeHere(@NotNull Set<Node> here, @NotNull Node node) {
        for (Edge downEdge : node.getDownEdges()) {
            if (!here.contains(downEdge.getDownNode())) {
                return false;
            }
        }
        return true;
    }

    @Nullable
    public GraphFragment getUpShortFragment(@NotNull Node startNode) {
        if (startNode.getType() == Node.Type.EDGE_NODE) {
            throw new IllegalArgumentException("small fragment may start only with COMMIT_NODE, but this node is: " + startNode);
        }

        Set<Node> downNodes = new HashSet<Node>();
        downNodes.add(startNode);
        Set<Node> notAddedNodes = new HashSet<Node>();
        addUpNodeToSet(notAddedNodes, startNode);

        Node endNode = null;

        int startRowIndex = startNode.getRowIndex() - 1;
        int lastIndex = 0;

        boolean isEnd = false;
        for (int currentRowIndex = startRowIndex; currentRowIndex >= lastIndex && !isEnd; currentRowIndex--) {
            for (Node node : graph.getNodeRows().get(currentRowIndex).getNodes()) {
                if (notAddedNodes.remove(node)) {
                    if (notAddedNodes.isEmpty() && node.getType() == Node.Type.COMMIT_NODE) {
                        if (allDownNodeHere(downNodes, node)) { // i.e. we found smallFragment
                            endNode = node;
                        }
                        isEnd = true;
                        break;
                    } else {
                        if (!allDownNodeHere(downNodes, node) || unhiddenNodes.get(node)) {
                            isEnd = true;
                        }
                        downNodes.add(node);
                        addUpNodeToSet(notAddedNodes, node);
                    }
                }
            }
        }
        if (endNode == null) {
            return null;
        } else {
            downNodes.remove(startNode);
            return new SimpleGraphFragment(endNode, startNode, downNodes);
        }
    }



}

@@ -535,16 +535,71 @@
 rator %7B%0A
+    private final static int MAX_FRAGMENT_SIZE = 100;%0A%0A
     priv
@@ -2138,16 +2138,44 @@
 ) - 1;%0A%0A
+        int countNodes = 0;%0A
         
@@ -2434,32 +2434,215 @@
 remove(node)) %7B%0A
+                    countNodes++;%0A                    if (countNodes %3E MAX_FRAGMENT_SIZE) %7B%0A                        isEnd = true;%0A                        break;%0A                    %7D%0A
                 

add MaxFragmentSize in ShortFragment