package poussecafe.doc;

import java.util.Objects;
import poussecafe.doc.graph.Node;
import poussecafe.doc.graph.NodeStyle;
import poussecafe.doc.graph.UndirectedEdge;
import poussecafe.doc.graph.UndirectedGraph;
import poussecafe.doc.model.aggregatedoc.AggregateDoc;
import poussecafe.doc.model.aggregatedoc.AggregateDocId;
import poussecafe.doc.model.aggregatedoc.AggregateDocRepository;
import poussecafe.doc.model.moduledoc.ModuleDoc;
import poussecafe.doc.model.relation.Component;
import poussecafe.doc.model.relation.ComponentType;
import poussecafe.doc.model.relation.Relation;
import poussecafe.doc.model.relation.RelationRepository;

public class ModuleGraphFactory {

    public static class Builder {

        private ModuleGraphFactory factory = new ModuleGraphFactory();

        public Builder moduleDoc(ModuleDoc moduleDoc) {
            factory.moduleDoc = moduleDoc;
            return this;
        }

        public Builder relationRepository(RelationRepository relationRepository) {
            factory.relationRepository = relationRepository;
            return this;
        }

        public Builder aggregateDocRepository(AggregateDocRepository aggregateDocRepository) {
            factory.aggregateDocRepository = aggregateDocRepository;
            return this;
        }

        public ModuleGraphFactory build() {
            Objects.requireNonNull(factory.moduleDoc);
            Objects.requireNonNull(factory.relationRepository);
            Objects.requireNonNull(factory.aggregateDocRepository);
            return factory;
        }
    }

    private ModuleGraphFactory() {

    }

    public UndirectedGraph buildGraph() {
        addSimpleAggregates();
        return graph;
    }

    private ModuleDoc moduleDoc;

    private UndirectedGraph graph = new UndirectedGraph();

    private void addSimpleAggregates() {
        for (AggregateDoc aggregateDoc : aggregateDocRepository.findByModule(moduleDoc.attributes().identifier().value())) {
            addSimpleAggregate(aggregateDoc);
            addAggregateRelations(aggregateDoc);
        }
    }

    private AggregateDocRepository aggregateDocRepository;

    private void addSimpleAggregate(AggregateDoc aggregateDoc) {
        Node node = Node.box(aggregateDoc.attributes().moduleComponentDoc().value().componentDoc().name());
        node.setStyle(NodeStyle.BOLD);
        graph.getNodesAndEdges().addNode(node);
    }

    private void addAggregateRelations(AggregateDoc aggregateDoc) {
        addAggregateRelations(aggregateDoc, aggregateDoc.className());
    }

    private void addAggregateRelations(AggregateDoc aggregateDoc, String fromClassName) {
        for(Relation relation : relationRepository.findWithFromClassName(fromClassName)) {
            if(relation.toComponent().type() == ComponentType.AGGREGATE) {
                AggregateDoc otherAggregate = aggregateDocRepository.get(AggregateDocId.ofClassName(relation.toComponent().className()));
                if(!aggregateDoc.className().equals(otherAggregate.className()) &&
                        otherAggregate.attributes().moduleComponentDoc().value().moduleDocId().equals(moduleDoc.attributes().identifier().value())) {
                    UndirectedEdge edge = UndirectedEdge
                            .solidEdge(aggregateDoc.attributes().moduleComponentDoc().value().componentDoc().name(),
                                    name(relation.toComponent()));
                    graph.getNodesAndEdges().addEdge(edge);
                }
            } else {
                addAggregateRelations(aggregateDoc, relation.toComponent().className());
            }
        }
    }

    private RelationRepository relationRepository;

    private String name(Component component) {
        switch(component.type()) {
        case AGGREGATE:
            return aggregateDocRepository.get(AggregateDocId.ofClassName(component.className())).attributes().moduleComponentDoc().value().componentDoc().name();
        default:
            throw new IllegalArgumentException("Unsupported component type " + component.type());
        }
    }
}

@@ -35,23 +35,71 @@
 va.util.
-Objects
+HashSet;%0Aimport java.util.Objects;%0Aimport java.util.Set
 ;%0Aimport
@@ -2602,107 +2602,333 @@
 me()
-);%0A    %7D%0A%0A    private void addAggregateRelations(AggregateDoc aggregateDoc, String fromClassName) %7B
+, new ExplorationState());%0A    %7D%0A%0A    private class ExplorationState %7B%0A%0A        Set%3CString%3E exploredComponends = new HashSet%3C%3E();%0A    %7D%0A%0A    private void addAggregateRelations(AggregateDoc aggregateDoc, String fromClassName, ExplorationState explorationState) %7B%0A        explorationState.exploredComponends.add(fromClassName);
 %0A   
@@ -3798,16 +3798,102 @@
   %7D else
+ if(!explorationState.exploredComponends.contains(relation.toComponent().className()))
  %7B%0A     
@@ -3969,24 +3969,42 @@
 .className()
+, explorationState
 );%0A         
@@ -4136,14 +4136,10 @@
     
-switch
+if
 (com
@@ -4155,25 +4155,26 @@
 pe()
-) %7B%0A        case 
+ == ComponentType.
 AGGR
@@ -4178,17 +4178,19 @@
 GGREGATE
-:
+) %7B
 %0A       
@@ -4356,16 +4356,16 @@
     
-default:
+%7D else %7B
 %0A   

Fix infinite recursion when building Module graph.