package network.undirected_network;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import network.network.Network;

public class UndirectedNetwork<Vertex extends Comparable<? super Vertex>> extends Network<Vertex> {
	
	public UndirectedNetwork() {
		super();
	}
	
	public UndirectedNetwork(UndirectedNetwork<Vertex> network) {
		super(network);
	}
	
	public boolean equals(Object object) {
		if (object == null) return false;
		if (object == this) return true;
		if (!(object instanceof UndirectedNetwork)) return false;
		
		UndirectedNetwork<?> undirectedNetwork = (UndirectedNetwork<?>) object;
		
		return this.adjacencyMap.equals(undirectedNetwork.adjacencyMap);
	}
	
	@Override
	public boolean addEdge(Vertex v1, Vertex v2, Double weight) {
		return super.addEdge(v1, v2, weight) && super.addEdge(v2, v1, weight);
	}
	
	@Override
	public boolean removeEdge(Vertex v1, Vertex v2) {
		return super.removeEdge(v1, v2) && super.removeEdge(v2, v1);
	}
	
	public boolean isConnected() {
		Vertex v = adjacencyMap.firstKey();
		Iterator<Vertex> it = new BreadthFirstIterator(v);
		int counter = 0;
		while (it.hasNext()) counter++;
		if (counter < adjacencyMap.size()) return false;
		return true;
	}
	
	/*
	 * TOTALE 2024-01-16
	 * Implementare un metodo interno alla classe UndirectedNetwork 
	 * che aggiunge il numero minimo di archi (con peso 1.0) necessari per rendere il grafo connesso corrente.
	 */
	/*
	 * Spiegazione: 
	 * se il grafo ha k componenti connesse,
	 * servono almeno k - 1 archi per renderlo connesso.
	 */
	public void connect() {
		// Verifica se ci sono elementi nel grafo
		if (adjacencyMap.isEmpty()) return;
		
		// Otteniamo il primo vertice per effettuare una BFS
		Vertex SS = adjacencyMap.firstKey();
		
		// Segnamo i vertici connessi e quelli non connessi
		TreeSet<Vertex> verticiTotali = new TreeSet<Vertex>(adjacencyMap.keySet());
		TreeSet<Vertex> verticiConnessi = new TreeSet<Vertex>();
		TreeSet<Vertex> verticiSconnessi = new TreeSet<Vertex>();
		
		// Otteniamo i vertici connessi tramite una BFS
		Iterator<Vertex> it = breadthFirstIterator(SS);
		while (it.hasNext()) verticiConnessi.add(it.next());
		
		// Otteniamo i vertici non connessi
		verticiSconnessi.addAll(verticiTotali);
		verticiSconnessi.removeAll(verticiConnessi);
		
		// Colleghiamo tutti i vertici non connessi minimamente
		while (!verticiSconnessi.isEmpty()) {
			Vertex verticeSconnesso = verticiSconnessi.first();
			this.addEdge(SS, verticeSconnesso, 1.0);
			
			Iterator<Vertex> itNuoviConnessi = breadthFirstIterator(verticeSconnesso);
			while (itNuoviConnessi.hasNext()) verticiConnessi.add(itNuoviConnessi.next());
			
			verticiSconnessi.clear();
			verticiSconnessi.addAll(verticiTotali);
			verticiSconnessi.removeAll(verticiConnessi);
		}
	}
	
	/*
	 * TOTALE 2024-01-30
	 * Implementare un metodo interno alla classe UndirectedNetwork che restituisce 
	 * il grado del grafo corrente, ovvero il massimo dei gradi dei suoi nodi.
	 */
	public int graphDegree() {
		// Controllo se il grafo è vuoto
		if (adjacencyMap.isEmpty()) return 0;
		// Conto il grado massimo
		Iterator<Vertex> iterator = iterator();
		int maxDeg = adjacencyMap.get(iterator.next()).size();
		while (iterator.hasNext()) {
			int curSize = adjacencyMap.get(iterator.next()).size();
			if (curSize > maxDeg) maxDeg = curSize;
		}
		return maxDeg;
	}
}
