package de.tu_darmstadt.tk.SmartHomeNetworkSim.control;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.ConfigurationManager;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;

/**
 * Controller which allows access to the configuration
 *
 * @author Andreas T. Meyer-Berg
 */
public class SettingsController {

	/**
	 * Model which is configured
	 */
	private Model model;
	/**
	 * Controller
	 */
	private Controller controller;
	
	/**
	 * Network Tree Controller
	 */
	private NetworkTreeSettingsController networkTreeSettings;
	
	/**
	 * Link ColorController
	 */
	private LinkColorController linkColors;
	/**
	 * @param model model which stores the configuration
	 * @param controller Main Controller for updates etc.
	 */
	public SettingsController(Model model, Controller controller) {
		this.model = model;
		this.controller = controller;
		networkTreeSettings = new NetworkTreeSettingsController(model,controller);
		linkColors = new LinkColorController(model, controller);
	}
	
	/**
	 * Returns the networkTreeSettingsController
	 * 
	 * @return NetworkTreeSettingsController
	 */
	public NetworkTreeSettingsController getNetworkTreeSettingsController(){
		return networkTreeSettings;
	}
	
	/**
	 * @return the linkColors
	 */
	public LinkColorController getLinkColors() {
		return linkColors;
	}

	/**
	 * Returns the ConfigurationManager
	 * 
	 * @return ConfigurationManager of the program
	 */
	
	public ConfigurationManager getConfigurationManager(){
		return model.getConfigurator();
	}
	
	/**
	 * @return the showConnections
	 */
	public boolean isShowConnections() {
		return model.getConfigurator().getVisualizationConfiguration().isShowConnections();
	}

	/**
	 * @param showConnections the showConnections to set
	 */
	public void setShowConnections(boolean showConnections) {
		model.getConfigurator().getVisualizationConfiguration().setShowConnections(showConnections);
		controller.notifyObservers();
	}

	/**
	 * @return the showLinks
	 */
	public boolean isShowLinks() {
		return model.getConfigurator().getVisualizationConfiguration().isShowLinks();
	}

	/**
	 * @param showLinks the showLinks to set
	 */
	public void setShowLinks(boolean showLinks) {
		model.getConfigurator().getVisualizationConfiguration().setShowLinks(showLinks);
		controller.notifyObservers();
	}

	/**
	 * @return the showSmartDevices
	 */
	public boolean isShowSmartDevices() {
		return model.getConfigurator().getVisualizationConfiguration().isShowSmartDevices();
	}

	/**
	 * @param showSmartDevices the showSmartDevices to set
	 */
	public void setShowSmartDevices(boolean showSmartDevices) {
		model.getConfigurator().getVisualizationConfiguration().setShowSmartDevices(showSmartDevices);
		controller.notifyObservers();
	}

	/**
	 * @return the showSmartDeviceNames
	 */
	public boolean isShowSmartDeviceNames() {
		return model.getConfigurator().getVisualizationConfiguration().isShowSmartDeviceNames();
	}

	/**
	 * @param showSmartDeviceNames the showSmartDeviceNames to set
	 */
	public void setShowSmartDeviceNames(boolean showSmartDeviceNames) {
		model.getConfigurator().getVisualizationConfiguration().setShowSmartDeviceNames(showSmartDeviceNames);
		controller.notifyObservers();
	}

	/**
	 * @return the showTerminatedConnections
	 */
	public boolean isShowTerminatedConnections() {
		return model.getConfigurator().getVisualizationConfiguration().isShowTerminatedConnections();
	}

	/**
	 * @param showTerminatedConnections the showTerminatedConnections to set
	 */
	public void setShowTerminatedConnections(boolean showTerminatedConnections) {
		model.getConfigurator().getVisualizationConfiguration().setShowTerminatedConnections(showTerminatedConnections);
		controller.notifyObservers();
	}

	/**
	 * @return the deviceVisualizationRadius
	 */
	public int getDeviceVisualizationRadius() {
		return model.getConfigurator().getVisualizationConfiguration().getDeviceVisualizationRadius();
	}

	/**
	 * @param deviceVisualizationRadius the deviceVisualizationRadius to set
	 */
	public void setDeviceVisualizationRadius(int deviceVisualizationRadius) {
		model.getConfigurator().getVisualizationConfiguration().setDeviceVisualizationRadius(deviceVisualizationRadius);
		controller.getNetworkController().validateDevicePosition();
		controller.notifyObservers();
	}

	/**
	 * @return the linkRadius
	 */
	public int getLinkRadius() {
		return model.getConfigurator().getVisualizationConfiguration().getLinkRadius();
	}

	/**
	 * @param linkRadius the linkRadius to set
	 */
	public void setLinkRadius(int linkRadius) {
		model.getConfigurator().getVisualizationConfiguration().setLinkRadius(linkRadius);
		controller.notifyObservers();
	}
	

	/**
	 * @return the showLinkToolTips
	 */
	public boolean isShowLinkToolTips() {
		return model.getConfigurator().getVisualizationConfiguration().isShowLinkToolTips();
	}

	/**
	 * @param showLinkToolTips the showLinkToolTips to set
	 */
	public void setShowLinkToolTips(boolean showLinkToolTips) {
		model.getConfigurator().getVisualizationConfiguration().setShowLinkToolTips(showLinkToolTips);
		controller.notifyObservers();
	}
	
	/**
	 * @return the showLinkNameList
	 */
	public boolean isShowLinkNameList() {
		return model.getConfigurator().getVisualizationConfiguration().isShowLinkNameList();
	}

	/**
	 * @param showLinkNameList the showLinkNameList to set
	 */
	public void setShowLinkNameList(boolean showLinkNameList) {
		model.getConfigurator().getVisualizationConfiguration().setShowLinkNameList(showLinkNameList);
		controller.notifyObservers();
	}
	
	/**
	 * @return the debugModus
	 */
	public boolean isDebugModus() {
		return model.getConfigurator().getVisualizationConfiguration().isDebugModus();
	}

	/**
	 * @param debugModus the debugModus to set
	 */
	public void setDebugModus(boolean debugModus) {
		model.getConfigurator().getVisualizationConfiguration().setDebugModus(debugModus);
		controller.notifyObservers();
	}
	
	/**
	 * Changes the dimension of the model, without scaling the SmartDevice
	 * positions
	 * 
	 * @param width
	 *            the new width to set
	 * @param height
	 *            the new height to set
	 * @param depth
	 *            the new depth to set
	 */
	public void setDimension(int width, int height, int depth) {
		setDimension(width, height, depth, false);
	}

	/**
	 * Changes the dimension of the model and moves SmartDevices to new relative
	 * positions if {@code stretchModel} is true
	 * 
	 * @param width
	 *            the new width to set
	 * @param height
	 *            the new height to set
	 * @param depth
	 *            the new depth to set
	 * @param stretchModel
	 *            true, if smartDevice positions should be stretched
	 */
	public void setDimension(int width, int height, int depth, boolean stretchModel) {
		// Don't allow to small Models
		if (width < 400)
			width = 400;
		if (height < 400)
			height = 400;
		if (depth < 400)
			depth = 400;
	
		if (stretchModel) {
			/**
			 * Factor that changes the x position. Example: 1 -> positions stay
			 * the same 0.5 -> x position is halved 2 -> x position is doubled
			 */
			double width_factor = ((double) width) / ((double) getWidth());
			/**
			 * Factor that changes the y position. Example: 1 -> positions stay
			 * the same 0.5 -> y position is halved 2 -> y position is doubled
			 */
			double height_factor = ((double) height) / ((double) getHeight());
			/**
			 * Factor that changes the z position. Example: 1 -> positions stay
			 * the same 0.5 -> z position is halved 2 -> z position is doubled
			 */
			double depth_factor = ((double) depth) / ((double) getDepth());
			// Update all device positions
			for (SmartDevice d : model.getDevices()) {
				if (width_factor != 1.0)
					d.setX(scalePos(d.getX(), width_factor, width));
				if (height_factor != 1.0)
					d.setY(scalePos(d.getY(), height_factor, height));
				if (depth_factor != 1.0)
					d.setZ(scalePos(d.getZ(), depth_factor, depth));
			}
		}
		setWidth(width);
		setHeight(height);
		setDepth(depth);
	}

	/**
	 * Calculates the new scaled position, which should be in Bounds:
	 * {@code radius < newPosition <
	 * (upperBound - radius)}
	 * 
	 * @param oldPosition
	 *            previous position that should be scaled
	 * @param factor
	 *            how much it is stretched/shrunk
	 * @param upperBound
	 *            upper bound of the frame, which should not be exceeded
	 * @return new position that was calculated
	 */
	int scalePos(int oldPosition, double factor, int upperBound) {
		/**
		 * New Position that should be validated and returned
		 */
		int newPosition = (int) Math.round(factor * oldPosition);
		// Update if it moves out of the frame
		if (newPosition < getDeviceVisualizationRadius())
			newPosition = getDeviceVisualizationRadius();
		if (newPosition >= upperBound - getDeviceVisualizationRadius())
			newPosition = upperBound - getDeviceVisualizationRadius();
	
		return newPosition;
	}

	/**
	 * @return the width
	 */
	public int getWidth() {
		return model.getWidth();
	}

	/**
	 * @param width the width to set
	 */
	public void setWidth(int width) {
		model.setWidth(width);
	}

	/**
	 * @return the height
	 */
	public int getHeight() {
		return model.getHeight();
	}

	/**
	 * @param height the height to set
	 */
	public void setHeight(int height) {
		model.setHeight(height);
	}
	
	/**
	 * @return the depth
	 */
	public int getDepth() {
		return model.getDepth();
	}

	/**
	 * @param depth the depth to set
	 */
	public void setDepth(int depth) {
		model.setDepth(depth);
	}
}
