// Point : Forest to Tree
  //
  // While T has more than one component
  //  For each component C of T :
  //   Begin with an empty set of edges S
  //   For each vertex v in C :
  //    Find the cheapest edge from v to a vertex outside of C, and add it to S
  //   Add the cheapest edge in S to T
  public WeightUnDirectedGraph MSTBySollin() {

    LinkedList<WeightUnDirectedGraph> graphLinkedList = new LinkedList<>();

    for (int i = 0; i < vertices.size(); i++) {
      graphLinkedList.add(new WeightUnDirectedGraph());
    }

    // 각각의 vertex에 대해서 graph라고 만듦
    for (int i = 0; i < vertices.size(); i++) {
      graphLinkedList.get(i).insertVertex(new Data(vertices.get(i).getData().id));
    }

    int index = 0;
    WeightUnDirectedGraph currentGraph;

    // While T has more than one component:
    while (graphLinkedList.size() > 1) {

      if (index >= graphLinkedList.size()) index = 0;

      // For each component C of T:
      currentGraph = graphLinkedList.get(index);

      // Begin with an empty set of edges S
      LinkedList<Edge> edgeList = new LinkedList<>();

      // For each vertex v in C:
      for (Vertex currentVertex : currentGraph.vertices) {

        // Find the cheapest edge from v to a vertex outside of C, and add it to S

        // 현재 큰 그래프에서 찾음
        Vertex vertex = retrieveVertex(currentVertex.getData().id);

        for (Edge edge : vertex.getEdges()) {

          if (currentGraph.retrieveVertex(edge.getToVertex().getData().id) == null)
            insertEdgeToList(edgeList, edge);
        }
      }

      int graphIndex = findGraphIndex(graphLinkedList, edgeList.getFirst().getToVertex());

      // Add the cheapest edge in S to T
      if (mergeGraph(currentGraph, graphLinkedList.get(graphIndex), edgeList.removeFirst()))
        graphLinkedList.remove(graphIndex);

      index++;
    }

    return graphLinkedList.getFirst();
  }
  // Point : Vertex
  // 1. Remove Edge from PQ
  // 2. check edge's toVertex is in Graph
  // 3. if toVertex is not exist then add that vertex to the graph and edges
  // 3-1. if to Vertex is exist in Graph then ignore this Edge
  public WeightUnDirectedGraph MSTByPrim() {

    if (vertices.size() == 0) return null;

    WeightUnDirectedGraph tmpGraph = new WeightUnDirectedGraph();

    LinkedList<Edge> edgeList = new LinkedList<>();

    // Insert First Vertex to Graph
    tmpGraph.insertVertex(new Data(vertices.getFirst().getData().id));

    // Get First tmpGraph Vertex
    Vertex currentVertex = vertices.getFirst();
    Vertex nextVertex;

    // Insert First Vertex Edges
    for (Edge edge : currentVertex.getEdges()) {
      insertEdgeToList(edgeList, edge);
    }

    // keep make graph(actually tree) until edgeList size get to 0
    while (edgeList.size() != 0) {

      // First One is lowest Weight Edge
      Edge edge = edgeList.removeFirst();

      // Vertex Exist => Already lowest weight path exist
      if (tmpGraph.retrieveVertex(edge.getToVertex().getData().id) != null) continue;

      // insert Vertex
      tmpGraph.insertVertex(new Data(edge.getToVertex().getData().id));

      nextVertex = edge.getToVertex();

      // insert edge between two vertices
      tmpGraph.insertEdge(
          tmpGraph.retrieveVertex(edge.getFromVertex().getData().id),
          tmpGraph.retrieveVertex(edge.getToVertex().getData().id),
          edge.getWeight());

      // insert next Vertex's Edges
      for (Edge tmpEdge : nextVertex.getEdges()) {
        insertEdgeToList(edgeList, tmpEdge);
      }
    }
    return tmpGraph;
  }
  // merge toGraph and fromGraph, and put edge to the toGraph
  private boolean mergeGraph(
      WeightUnDirectedGraph toGraph, WeightUnDirectedGraph fromGraph, Edge edge) {

    // Add "fromGraph" To "toGraph"
    // Both graphs are disjoint!!
    LinkedList<Edge> edgeList = new LinkedList<>();

    // move all vertices from fromGraph to toGraph
    for (int i = 0; i < fromGraph.size; i++) {

      if (!toGraph.vertices.contains(fromGraph.vertices.get(i)))
        toGraph.insertVertex(new Data(fromGraph.vertices.get(i).getData().id));

      // add all edges from fromGraph to toGraph
      for (Edge tmpEdge : fromGraph.vertices.get(i).getEdges()) {

        insertEdgeToList(edgeList, tmpEdge);
      }
    } // add all vertices to toGraph

    // insert all edges to the toGraph
    for (Edge tmpEdge : edgeList) {

      toGraph.insertEdge(
          toGraph.retrieveVertex(tmpEdge.getFromVertex().getData().id),
          toGraph.retrieveVertex(tmpEdge.getToVertex().getData().id),
          tmpEdge.getWeight());
    }

    // insert "edge"(see above parameter) to the toGraph
    toGraph.insertEdge(
        toGraph.retrieveVertex(edge.getFromVertex().getData().id),
        toGraph.retrieveVertex(edge.getToVertex().getData().id),
        edge.getWeight());

    return true;
  }
  // Point : Edge
  // 1. Put edges to the edgeList(sort by weight) (frankly it is Minimum Heap)
  // 2. remove edge element from heap and add to the graph
  // 3. if there is a cycle then remove that edge(which we inserted at step 2)
  public WeightUnDirectedGraph MSTByKruskal() {

    if (vertices.size() == 0) return null;

    WeightUnDirectedGraph tmpGraph = new WeightUnDirectedGraph();

    // Use this as a Heap
    LinkedList<Edge> edgeList = new LinkedList<>();

    // Put edges to the edgeList(sort by weight)
    for (Vertex currentVertex : vertices) {
      for (Edge currentEdge : currentVertex.getEdges()) {
        insertEdgeToList(edgeList, currentEdge);
      }
    }

    // Make new Graph(Tree) by using edgeList
    while (!edgeList.isEmpty()) {

      Edge edge = edgeList.removeFirst();

      // Make new vertex for tmpGraph
      if (tmpGraph.retrieveVertex(edge.getFromVertex().getData().id) == null)
        tmpGraph.insertVertex(new Data(edge.getFromVertex().getData().id));

      if (tmpGraph.retrieveVertex(edge.getToVertex().getData().id) == null)
        tmpGraph.insertVertex(new Data(edge.getToVertex().getData().id));

      // insert Edge and Weight between vertices
      tmpGraph.insertEdge(
          tmpGraph.retrieveVertex(edge.getFromVertex().getData().id),
          tmpGraph.retrieveVertex(edge.getToVertex().getData().id),
          edge.getWeight());

      // Check if there is a cycle
      if (tmpGraph.hasCycle()) {
        // if there is a cycle
        // then delete edge
        tmpGraph.deleteEdge(
            tmpGraph.retrieveVertex(edge.getFromVertex().getData().id),
            tmpGraph.retrieveVertex(edge.getToVertex().getData().id));
        System.out.println("Cycle Exist");
      }
    }
    return tmpGraph;
  }