package edu.isi.karma.modeling.steiner.topk;


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

/**
 * This class serves as the main building block of a Steiner tree.
 * Additionally to the the class Entity a SteinerNode has also the edges that
 * contain it and a predecessor node from which the SteinerNode is reached.
 * @author kasneci
 *
 */
public class SteinerNode extends Entity {
	
	/*
	 * Each edge has this Steiner Node either as sink or as source node.
	 */
	protected TreeSet<SteinerEdge> edges;
	
	/*
	 * needed to reconstruct paths
	 */
	protected SteinerNode predecessor ;
	protected String relationToPredecessor;
	protected float weightToPredecessor;
	protected boolean wasArg1;
	protected double [] distancesToSources=new double [2];
	
	protected SteinerEdge predecessorLink;
	/*
	 * for BANKS II
	 */
	
	public SteinerNode(String id){
		super(id);
		edges= new TreeSet<>();		
	}
	
	/**
	 * copies only the name of the node
	 * @return
	 */
	public SteinerNode copy(){
		
		SteinerNode newNode = new SteinerNode(this.name);
		return newNode;
	}
	
	public int hashCode(){
		return name.hashCode();
	}
	
	
	
	/**
	 * sets n as the predecessor of this node, during a search process...
	 * ...needed for rebuilding issues...
	 * @param n
	 */
	public void setPredecessor(SteinerNode n){
		predecessor=n;
	}
	
	/**
	 * @param e
	 * @return the neighbor connected to this node through the steiner edge e
	 */
	public SteinerNode getNeighborInEdge(SteinerEdge e){
		if(e.sourceNode.equals(this))
			return e.sinkNode;
		else if(e.sinkNode.equals(this))
			return e.sourceNode;
		else return null;
	}
	
	/**
	 * @param n
	 * @return the steiner edge that connects this node to n
	 */
	public SteinerEdge getEdgeToNode(SteinerNode n){
		SteinerEdge e=null;
		for(SteinerEdge edge: edges){
			if(edge.sourceNode.equals(n)||edge.sinkNode.equals(n)){
				e=edge;
				break;
			}
		}
		return e;
	}
	
	public String getEdgeLabelToNode(SteinerNode n){
		return getEdgeToNode(n).label().name;
	}
	
	/**
	 * 
	 * @return all neighbors of this steiner node
	 */
	public TreeSet<SteinerNode> getNeighbors(){
		TreeSet<SteinerNode> ts = new TreeSet<>();
		for(SteinerEdge e: edges){
			if(e.sourceNode.equals(this))
				ts.add(e.sinkNode);
			if(e.sinkNode.equals(this))
				ts.add(e.sourceNode);
		}
		return ts;
	}
	
	/**
	 * 
	 * @param exceptForEdge
	 * @return return all neighbors of this node except for the node connected to this node
	 * through exceptForEdge
	 */
	public TreeSet<SteinerNode> getNeighbors(SteinerEdge exceptForEdge){
		TreeSet<SteinerNode> ts = new TreeSet<>();
		if(exceptForEdge==null){
			return getNeighbors();
		}
		else
			for(SteinerEdge e: edges)
				if(!e.equals(exceptForEdge)){
					if(e.sourceNode.equals(this))
						ts.add(e.sinkNode);
					if(e.sinkNode.equals(this))
						ts.add(e.sourceNode);
					
				}
		return ts;
	}
	
	public void addEdge(SteinerEdge edge){
		if(edge.sourceNode.equals(this)){
			edges.add(edge);
			edge.sinkNode.edges.add(edge);
		}
		if( edge.sinkNode.equals(this)){
			edges.add(edge);
			edge.sourceNode.edges.add(edge);
		}
	}
	
	/**
	 * 
	 * @param n the new node connected to this node through the new steiner edge to be added
	 * @param isSourceNode boolean variable indicating whether n is the source node
	 * in the new steiner edge (which is going to be added)
	 * @param l label of the new edge
	 * @param weight weight of the new edge
	 */
	public void addEdge(SteinerNode n, boolean isSourceNode, String l, float weight){
		if(isSourceNode) addEdge(new SteinerEdge(n,l,this,weight));
		else addEdge(new SteinerEdge(this, l, n,weight));
	}
	
	/**
	 * deletes all edges of this node
	 *
	 */
	public void deleteEdges(){
		edges= new TreeSet<>();
	}
	
	/**
	 * @param e steiner edge to be removed
	 */
	public void deleteEdge(SteinerEdge e){
		Set<SteinerEdge> badEdges= new TreeSet<>();
		for(SteinerEdge e1: edges)
			if(e1.sinkNode.equals(e.sinkNode)&& e1.sourceNode.equals(e.sourceNode))
				badEdges.add(e1);
		edges.removeAll(badEdges);
	}
	
	
	/**
	 * 
	 * @return number of edges this node is involved in
	 */
	public int getDegree(){
		return edges.size();
	}
	
	/**
	 * 
	 * @return true if this node has more than two edges
	 */
	public boolean isFixedNode(){
		return edges.size()>=3;
	}
	
	
	public String getNodeId(){
		return name;
	}


	public String getRelationToPredecessor() {
		return relationToPredecessor;
	}

	public void setRelationToPredecessor(String relationToPredecessor) {
		this.relationToPredecessor = relationToPredecessor;
	}

	public boolean isWasArg1() {
		return wasArg1;
	}

	public void setWasArg1(boolean wasArg1) {
		this.wasArg1 = wasArg1;
	}

	public float getWeightToPredecessor() {
		return weightToPredecessor;
	}

	public void setWeightToPredecessor(float weightToPredecessor) {
		this.weightToPredecessor = weightToPredecessor;
	}

	public SteinerNode getPredecessor() {
		return predecessor;
	}

	public TreeSet<SteinerEdge> getEdges() {
		return edges;
	}
}
