package io.github.programminglife2016.pl1_2016.collapser;

import io.github.programminglife2016.pl1_2016.parser.nodes.Node;
import io.github.programminglife2016.pl1_2016.parser.nodes.NodeCollection;

import java.util.*;

/**
 * Created by ravishivam on 15-5-16.
 */
public class BubbleDetector {
    private static final int NOT_A_BUBBLE = 0;

    private static final int BUBBLE_DETECTED = 1;

    private static final int FOUND_MORE_GENOMES = 2;

    private boolean[] visited;

    private NodeCollection collection;

    private List<Bubble> bubbleBoundaries;

    private int lastId;

    private int reachedLevel = 1;

    public BubbleDetector(NodeCollection collection) {
        this.visited = new boolean[collection.size() + 1];
        this.lastId = collection.size() + 1;
        initVisited(collection);
        this.collection = collection;
        this.bubbleBoundaries = new ArrayList<>();
    }

    public void findMultiLevelBubbles() {
        Map<Integer, List<Bubble>> levelBubbles = new HashMap<>();
        levelBubbles.put(1, findLevelBubbles(this.collection.get(1), null));
        this.reachedLevel++;
        for (int i = 0; i < 3; i++) {
            List<Bubble> currLevelList = new ArrayList<>();
            for (Bubble bubble : levelBubbles.get(reachedLevel - 1)) {
                for (Node node : bubble.getStartNode().getLinks()) {
                    List<Bubble> newBubbles = findLevelBubbles(node, bubble.getEndNode());
                    currLevelList.addAll(newBubbles);
                }
            }
            levelBubbles.put(reachedLevel,currLevelList);
            reachedLevel++;
        }
    }

    public List<Bubble> findLevelBubbles(Node startNode, Node endNode) {
        List<Bubble> levelCollection = new ArrayList<>();
        Map.Entry<Integer, Node> bubbleAt = searchBubble(startNode, startNode.getGenomes());
        while (bubbleAt.getValue() != endNode) {
            switch (bubbleAt.getKey()) {
                case BUBBLE_DETECTED :
                    Node value = bubbleAt.getValue();
                    handleDetectedBubble(startNode, value, levelCollection);
                    startNode = value;
                    bubbleAt = searchBubble(startNode, startNode.getGenomes());
                    break;
                case FOUND_MORE_GENOMES :
                    handleDetectedBubble(startNode, startNode, levelCollection);
                    startNode = bubbleAt.getValue();
                    bubbleAt = searchBubble(startNode, startNode.getGenomes());
                    break;
            }
            if (bubbleAt == null) {
                break;
            }
        }
        return levelCollection;
    }

    public Map.Entry<Integer, Node> searchBubble(Node source, Collection genomes) {
        visited[source.getId()] = true;
        List<Node> connectedTo = new ArrayList<>(source.getLinks());
        for (Node child : connectedTo) {
            int status = checkGenomeMatch(genomes, child.getGenomes());
            if (status != NOT_A_BUBBLE) {
                return new AbstractMap.SimpleEntry<>(status, child);
            }
            if (visited[child.getId()] == false) {
                return searchBubble(child, genomes);
            }
        }
        return null;
    }

    private void handleDetectedBubble(Node starting, Node value, List<Bubble> levelCollection) {
        Bubble found = new Bubble(lastId, starting, value);
        found.getStartNode().setContainerId(found.getId());
        found.getEndNode().setContainerId(found.getId());
        found.setZoomLevel(reachedLevel);
        levelCollection.add(found);
        lastId++;
    }

    private int checkGenomeMatch(Collection genomes, Set<String> secondSet) {
        if(genomes.equals(secondSet)) {
            return BUBBLE_DETECTED;
        }
        else if (isLargerSet(genomes, secondSet)){
            return FOUND_MORE_GENOMES;
        }
        else {
            return NOT_A_BUBBLE;
        }
    }

    private boolean isLargerSet(Collection genomes, Set<String> secondSet) {
        if (secondSet.size() < genomes.size()) {
            return false;
        }
        Collection<String> intersection = new HashSet<>(genomes);
        intersection.retainAll(new HashSet<>(secondSet));
        return intersection.equals(genomes);
    }

    private void initVisited(NodeCollection collection) {
        for (Node node : collection.values()) {
            visited[node.getId()] = false;
        }
    }

    public List<Bubble> getBubbleBoundaries() {
        return bubbleBoundaries;
    }
}

@@ -1286,24 +1286,185 @@
 vel - 1)) %7B%0A
+                if(bubble.getStartNode().getLinks().size()==0 %7C%7C bubble.getStartNode() == bubble.getEndNode()) %7B%0A                    continue;%0A                %7D%0A
             

Fix for bug causing null pointer exception