package de.tu_darmstadt.tk.SmartHomeNetworkSim.control;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Link;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Port;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.configuration.NetworkTreeNodeStatus;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.configuration.NetworkTreeSettings;

/**
 * Controller which allows simple manipulation of the NetworkTreePanel, changing visibility of single or multiple links/connections and Devices
 * 
 *
 * @author Andreas T. Meyer-Berg
 */
public class NetworkTreeSettingsController {
	/**
	 * Model to be manipulated
	 */
	private Model model;
	/**
	 * Controller which can be used
	 */
	private Controller controller;
	/**
	 * NetworkTreeSettings of the model
	 */
	private NetworkTreeSettings networkTreeSettings;
	/**
	 * Create a new NetworkTreeSettingsController, which controls the networkTree
	 * @param model model of the application
	 * @param controller man controller for manipulation
	 */
	public NetworkTreeSettingsController(Model model, Controller controller) {
		this.model = model;
		this.controller = controller;
		networkTreeSettings = this.model.getConfigurator().getNetworkTreeSettings();
	}
	
	/**
	 * Toggles visibility of the given object and all of its descendants
	 * @param o Object to be shown/hidden
	 */
	public void toggleVisibilityOf(Object o){
		/**
		 * o visible ?
		 */
		boolean visible = networkTreeSettings.getStatusOfObject(o).isVisible();
		setVisibilityOf(o, !visible);
	}
	
	/**
	 * Sets the visibility of Object {@code o} to {@code visible}, will also change visibility of all descendants
	 * @param o Object which should be set visible/hidden
	 * @param visible true then object will be shown, false then object will not be shown
	 */
	public void setVisibilityOf(Object o, boolean visible){
		if(o instanceof String){
			/**
			 * String node, is the top most -> Hide/Show the hole network
			 */
			getStatusOfObject(o).setVisible(visible);
			for(Link l:controller.getNetworkController().getLinks())
				setVisibilityOfLink(l, visible);
		}else if(o instanceof Link){
			setVisibilityOfLink((Link)o, visible);
		}else if(o instanceof Connection){
			setVisibilityOfConnection((Connection)o, visible);
		}else if(o instanceof SmartDevice){
			setVisibilityOfSmartDevice((SmartDevice)o, visible);
		}
		/**
		 * Notify observer
		 */
		controller.notifyObservers();
	}
	
	/**
	 * Change visibility of Device to the new value
	 * @param device device to hide/show
	 * @param isVisible new Visibility
	 */
	private void setVisibilityOfSmartDevice(SmartDevice device, boolean isVisible) {
		networkTreeSettings.getStatusOfObject(device).setVisible(isVisible);
	}
	
	/**
	 * Change visibility of Connection and all its Devices
	 * @param connection connection to hide/show
	 * @param isVisible new Visibility
	 */
	private void setVisibilityOfConnection(Connection connection, boolean isVisible) {
		networkTreeSettings.getStatusOfObject(connection).setVisible(isVisible);
		/**
		 * Update all devices
		 */
		for(Port port: connection.getParticipants()){
			if(port.getOwner()!=null){
				if(isVisible){
					networkTreeSettings.getStatusOfObject(port.getOwner()).setVisible(isVisible);
				}else{
					/**
					 * Just hide Devices which are in no further connection
					 */
					boolean found = false;
					for(Port c:port.getOwner().getPorts()){
						if(found)break;
						if(c.getConnection()!=connection&&c.getConnection()!=null && isVisible(c.getConnection())){
							found=true;
							break;
						}
					}
					networkTreeSettings.getStatusOfObject(port.getOwner()).setVisible(found);
				}
			}
		}
		
	}

	/**
	 * Set visibility of Link, and all its connections and devices
	 * @param link link, which should be shown/hidden
	 * @param visible whether it should be visible
	 */
	public void setVisibilityOfLink(Link link, boolean visible){
		networkTreeSettings.getStatusOfObject(link).setVisible(visible);
		/**
		 * Update all connections of this link
		 */
		for(Connection c: link.getConnections()){
			setVisibilityOfConnection(c,visible);
		}
	}
	
	/**
	 * Returns the Status for the given Object
	 * @param o Object, whose status should be returned
	 * @return Status of the object
	 */
	public NetworkTreeNodeStatus getStatusOfObject(Object o){
		return networkTreeSettings.getStatusOfObject(o);
	}
	
	/**
	 * Adds an status for the given Object
	 * @param o Object to be added
	 * @param status Status of the object
	 */
	public void addStatusOfObject(Object o, NetworkTreeNodeStatus status){
		networkTreeSettings.addStatusOfObject(o, status);
	}
	
	/**
	 * Toggle the expanded status, expanded nodes will be collapsed and collapsed ones will be expanded
	 * 
	 * @param o Object to be expanded / collapsed
	 */
	public void toggleExpanded(Object o){
		setExpanded(o, !networkTreeSettings.getStatusOfObject(o).isExpanded());
		controller.notifyObservers();
	}
	/**
	 * Set the expanded status of the object o to the value of isExpanded
	 * @param o Object to be expanded / collapsed
	 * @param isExpanded whether it should be expanded or collapsed
	 */
	public void setExpanded(Object o, boolean isExpanded){
		networkTreeSettings.getStatusOfObject(o).setExpanded(isExpanded);
		controller.notifyObservers();
	}
	
	/**
	 * Toggle visibility of an object
	 * @param o object to be shown/hidden
	 */
	public void toggleVisibilityNonRecursive(Object o){
		setVisibilityNonRecursive(o, !getStatusOfObject(o).isVisible());
		controller.notifyObservers();
	}

	/**
	 * Set Visibility of the object
	 * @param o object to be shown/hidden
	 * @param visible true if visible
	 */
	public void setVisibilityNonRecursive(Object o, boolean visible) {
		getStatusOfObject(o).setVisible(visible);
		controller.notifyObservers();
	}
	
	/**
	 * Returns true if the given object is visible
	 * @param o object which should be checked
	 * @return true if visible
	 */
	public boolean isVisible(Object o){
		return getStatusOfObject(o).isVisible();
	}
	
	/**
	 * Removes the status of the object o
	 * @param o Object, which status should be removed
	 */
	public void removeStatusOfObject(Object o){
		this.networkTreeSettings.removeStatusOfObject(o);
	}

}
