package de.tu_darmstadt.tk.SmartHomeNetworkSim.view.popups;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.NetworkController;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.ConnectionPrecision;
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.PrecisionLink;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Protocol;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.protocols.MQTT_protocol;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.simpleImplementation.SimpleLink;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.JComboBox;
import javax.swing.JButton;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

@SuppressWarnings("serial")
public class ConnectionCreationPanel extends JScrollPane {
	/**
	 * Panel which contains the content of this creation panel
	 */
	private JPanel content;

	/**
	 * Textfield which stores the Name of the connection
	 */
	private JTextField tfName;

	/**
	 * Parent window which contains this frame - for the close operation
	 */
	private Window frame;

	/**
	 * Connection which is edited
	 */
	private Connection connection;

	/**
	 * Index of the disconnected option in the cmbPortRoles Array
	 */
	private int disconnectedIndex;

	/**
	 * Ports which could participate in the connection
	 */
	private Port[] ports;

	/**
	 * ComboBox for link selection
	 */
	private JComboBox<String> cmbSelectedLink;

	/**
	 * ComboBox for connection selection
	 */
	private JComboBox<String> cmbConnection;

	/**
	 * ComboBox for the protocol selection
	 */
	private JComboBox<String> cmbProtocolType;

	/**
	 * ComboBoxes to allow role changes of the different Ports
	 */
	private JComboBox<String>[] cmbPortRoles;

	/**
	 * ComboBox for the status
	 */
	private JComboBox<String> cmbStatus;

	/**
	 * True if an existing connection is being edited, or false if a new one is
	 * being created
	 */
	private boolean edit;
	
	/**
	 * True if a new Link was created for this connection
	 */
	private boolean newLink = false;

	/**
	 * Last index which was selected in the protocolType ComboBox, to allow
	 * restore the selected Index in case of invalid protocols
	 */
	private int lastProtocolIndex = 0;

	/**
	 * Last index which was selected in the selectedLink ComboBox, to allow
	 * restore the selected Index in case of invalid links
	 */
	private int lastLinkIndex = -1;

	/**
	 * Last index which was selected in the selectedConnection ComboBox, to
	 * allow restore the selected Index in case of invalid Connection
	 */
	private int lastConnectionIndex = -1;

	/**
	 * Mutex for the protocol change, so the different Devices don't change
	 * their role, while their comboBoxes are updated. True if the protocol
	 * change is in progress and the cmbPortRoles should not update their role.
	 */
	private boolean protocolChange = false;
	/**
	 * Controller to manipulate the model
	 */
	private Controller controller;
	/**
	 * Network Controller
	 */
	private NetworkController network;
	/**
	 * List of available protocols: should be moved to the Model and accessed
	 * via controller
	 */
	private LinkedList<Class<? extends Protocol>> availableProtocols;
	/**
	 * List of available connections
	 */
	private LinkedList<Class<? extends Connection>> availableConnection;
	/**
	 * Textfield for the packet loss
	 */
	private JTextField tfPacketLossRate;

	/**
	 * Should not update if mutex is true
	 */
	private boolean mutex = true;
	private JTextField tfLabel;


	/**
	 * Creates a new ConnectionCreation panel
	 * @param connection connection which should be edited
	 * @param controller controller for manipulation
	 * @param frame parent frame
	 * @wbp.parser.constructor
	 */// @wbp.parser.constructor for Eclipse:WindowBuilder
	public ConnectionCreationPanel(Connection connection, Controller controller, Window frame) {
		this.controller = controller;
		this.connection = connection;
		this.network = controller.getNetworkController();
		this.frame = frame;
		ports = new Port[connection.getParticipants().size()];
		int i = 0;
		for (Port d : connection.getParticipants()) {
			// controller.addLinkToDevice(connection, d);
			this.ports[i++] = d;
		}
		edit = true;
		initializePanel();
	}

	public ConnectionCreationPanel(Collection<Port> ports, Link l, Controller controller, Window frame) {
		this.controller = controller;
		this.network = controller.getNetworkController();
		this.frame = frame;
		/**
		 * if link null -> create new link
		 */
		if(l == null){
			l = new PrecisionLink("New Link");
			for(Port device: ports){
				controller.getNetworkController().addLinkToDevice(l, device.getOwner());
			}
			newLink = true;
		}
		connection = new ConnectionPrecision(l, new MQTT_protocol());
		this.ports = new Port[ports.size()];
		int i = 0;
		for (Port d : ports) {
			// controller.addLinkToDevice(connection, d);
			this.ports[i++] = d;
		}
		edit = false;
		initializePanel();
	}

	@SuppressWarnings("unchecked")
	private void initializePanel() {
		// Init variables
		disconnectedIndex = connection.getProtocol().getNumberOfRoles();

		availableProtocols = controller.getImportController().getProtocols();
		availableConnection = controller.getImportController().getConnections();

		// Sets up window
		setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		this.setPreferredSize(new Dimension(600, 250 + ports.length * 20));
		content = new JPanel();
		content.setPreferredSize(new Dimension(600, 240 + ports.length * 20));
		this.setViewportView(content);
		content.setLayout(null);

		JLabel lblDescription = new JLabel("Connection Creation");
		lblDescription.setBounds(12, 0, 144, 16);
		content.add(lblDescription);

		JLabel lblName = new JLabel("Name:");
		lblName.setHorizontalAlignment(SwingConstants.RIGHT);
		lblName.setBounds(10, 10, 140, 20);
		content.add(lblName);

		tfName = new JTextField();
		tfName.setBounds(155, 10, 290, 20);
		content.add(tfName);
		tfName.setColumns(10);
		tfName.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (mutex) {
					return;
				}
				connection.setName(tfName.getText());
			}
		});

		JLabel lblSelectedLink = new JLabel("Selected link:");
		lblSelectedLink.setHorizontalAlignment(SwingConstants.RIGHT);
		lblSelectedLink.setBounds(10, 40, 140, 20);
		content.add(lblSelectedLink);

		// Check if model contains link
		if (connection.getLink() != null && !network.getLinks().contains(connection.getLink()))
			network.addLink(connection.getLink());

		cmbSelectedLink = new JComboBox<String>();
		cmbSelectedLink.setBounds(155, 40, 125, 20);
		content.add(cmbSelectedLink);

		cmbSelectedLink.addActionListener(a -> {
			if (mutex) {
				return;
			}
			int cmbSelectedLinkIndex = cmbSelectedLink.getSelectedIndex();
			if (cmbSelectedLinkIndex == -1)
				return;
			Link newLink = null;
			Iterator<Link> linkIterator = network.getLinks().iterator();
			for (int i = 0; i < cmbSelectedLinkIndex && linkIterator.hasNext(); i++) {
				linkIterator.next();
			}
			if (linkIterator.hasNext())
				newLink = linkIterator.next();
			if (!network.changeLinkOfConnection(connection, newLink)) {
				// if LinkChange failed: Restore
				cmbSelectedLink.setSelectedIndex(lastLinkIndex);
			} else {
				lastLinkIndex = cmbSelectedLinkIndex;
			}

		});

		JButton btnCreateLink = new JButton("Create Link");
		btnCreateLink.setBounds(290, 40, 155, 20);
		content.add(btnCreateLink);
		btnCreateLink.addActionListener(a -> {
			new LinkCreationDialog(
					connection.getParticipants().stream().map(lElem -> lElem.getOwner()).collect(Collectors.toList()),
					controller, content);
			// TODO: Refresh
		});

		JLabel lblConnectionType = new JLabel("Connection type:");
		lblConnectionType.setHorizontalAlignment(SwingConstants.RIGHT);
		lblConnectionType.setBounds(10, 70, 140, 20);
		content.add(lblConnectionType);

		cmbConnection = new JComboBox<String>();
		cmbConnection.setBounds(155, 70, 125, 20);
		content.add(cmbConnection);

		cmbConnection.addActionListener(a -> {
			if (mutex) {
				return;
			}
			cmbConnection.getSelectedIndex();
			int selectedIndex = cmbConnection.getSelectedIndex();
			if (lastConnectionIndex != selectedIndex && selectedIndex != -1) {
				Connection newConnection = network.changeConnectionType(connection,
						availableConnection.get(selectedIndex));
				if (newConnection == null) {
					System.out.println("Warning invalidConnection changed");
					cmbConnection.setSelectedIndex(lastConnectionIndex);
				} else {
					connection = newConnection;
					lastConnectionIndex = selectedIndex;
				}
			} else {
				cmbConnection.setSelectedIndex(lastConnectionIndex);
			}

		});

		JButton btnImportConnection = new JButton("Import Connection");
		btnImportConnection.setBounds(290, 70, 155, 20);
		content.add(btnImportConnection);
		btnImportConnection.addActionListener(a -> {
			ImportPopUp<Connection> popUp = new ImportPopUp<Connection>(this, Connection.class);
			try {
				Class<? extends Connection> imported = popUp.showPopUp();
				if (imported == null)
					return;
				if (controller.getImportController().addConnection(imported)) {
					refreshGUI();
				} else {
					JOptionPane.showMessageDialog(frame, "Import failed: Invalid Connection");
				}
			} catch (Exception e1) {
				JOptionPane.showMessageDialog(frame, "Import failed: " + e1.getMessage());
			}
		});

		JLabel lblProtocolType = new JLabel("Protocol Type:");
		lblProtocolType.setHorizontalAlignment(SwingConstants.RIGHT);
		lblProtocolType.setBounds(10, 100, 140, 20);
		content.add(lblProtocolType);

		cmbProtocolType = new JComboBox<String>();
		cmbProtocolType.setBounds(155, 100, 125, 20);
		content.add(cmbProtocolType);

		// Add Functionality for changing protocol
		cmbProtocolType.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (mutex)
					return;

				/**
				 * Old Protocol
				 */
				Protocol oldProtocol = connection.getProtocol();
				// Set Mutex to true, to disallow changes of roles during the
				// editing
				protocolChange = true;
				/**
				 * New Protocol which should be created and configured
				 */
				Protocol newProtocol = null;
				try {
					// Create new Instance of the protocol
					newProtocol = controller.getImportController().getProtocols()
							.get(cmbProtocolType.getSelectedIndex()).newInstance();
				} catch (InstantiationException | IllegalAccessException e1) {
					System.out.println("WARNING: Protocol could not be initialized");
				}
				if (newProtocol == null) {
					cmbProtocolType.setSelectedIndex(lastProtocolIndex);
					System.out.println("WARNING: Invalid Protocol Selected - restore last index");
				} else if (connection.setProtocol(newProtocol)) {
					/**
					 * New Roles as Strings
					 */
					String[] newRoles = newProtocol.getRoles();
					/**
					 * The old disconnected status
					 */
					int oldDisconnected = oldProtocol.getNumberOfRoles();
					// Update to new disconnected Index
					disconnectedIndex = newProtocol.getNumberOfRoles();
					// Update Protocol

					// Update lastProtocolIndex to new Index
					lastProtocolIndex = cmbProtocolType.getSelectedIndex();
					// Update Boxes
					for (int i = 0; i < cmbPortRoles.length; i++) {
						/**
						 * The previous selected Index
						 */
						int oldSelected = cmbPortRoles[i].getSelectedIndex();
						// Update Labels of the current Box
						cmbPortRoles[i].removeAllItems();
						for (int j = 0; j < newRoles.length; j++)
							cmbPortRoles[i].addItem(newRoles[j]);
						cmbPortRoles[i].addItem("Disconnected");
						// Try to update the Roles
						if (oldSelected < 0 || oldSelected >= disconnectedIndex || oldSelected == oldDisconnected) {
							// Invalid Index -> Disconnected
							network.removeDeviceFromConnectionAndProtocol(ports[i], connection);
							cmbPortRoles[i].setSelectedIndex(disconnectedIndex);
						} else {
							if (network.addDeviceToConnectionAndProtocol(ports[i], connection, oldSelected)) {
								// Set to Box to display role
								cmbPortRoles[i].setSelectedIndex(oldSelected);
							} else {
								// Could not be added -> Disconnected
								network.removeDeviceFromConnectionAndProtocol(ports[i], connection);
								cmbPortRoles[i].setSelectedIndex(disconnectedIndex);
							}
						}
					}
				} else {
					cmbProtocolType.setSelectedIndex(lastProtocolIndex);
				}
				// Set Mutex back to false - allow changes
				protocolChange = false;
			}
		});

		JButton btnImportProtocol = new JButton("Import Protocol");
		btnImportProtocol.setBounds(290, 100, 155, 20);
		content.add(btnImportProtocol);
		btnImportProtocol.addActionListener(a -> {
			ImportPopUp<Protocol> popUp = new ImportPopUp<Protocol>(this, Protocol.class);

			Class<? extends Protocol> imported = null;
			try {
				imported = popUp.showPopUp();
			} catch (Exception e1) {
				JOptionPane.showMessageDialog(frame, "Import failed: " + e1.getMessage());
				return;
			}
			if (imported == null) {
				// Import cancelled
				return;
			}
			// Add Protocol
			if (controller.getImportController().addProtocol(imported)) {
				// Update GUI
				refreshGUI();
			} else {
				JOptionPane.showMessageDialog(frame, "Import failed: Invalid Protocol");
				return;
			}
		});

		JLabel lblPacketlossrate = new JLabel("PacketLossRate:");
		lblPacketlossrate.setHorizontalAlignment(SwingConstants.RIGHT);
		lblPacketlossrate.setBounds(10, 130, 140, 20);
		lblPacketlossrate.setToolTipText("Probability of Packet loss, should be a Double in [0.0,1.0]");
		content.add(lblPacketlossrate);

		tfPacketLossRate = new JTextField();
		tfPacketLossRate.setBounds(155, 130, 90, 20);
		content.add(tfPacketLossRate);
		tfPacketLossRate.setColumns(10);
		tfPacketLossRate.getDocument().addDocumentListener(new DocumentListener() {

			@Override
			public void removeUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			@Override
			public void changedUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			private void updateConnectionAndField() {
				if(mutex)
					return;
				try {
					double newRate = Double.parseDouble(tfPacketLossRate.getText());
					if (newRate >= 0.0 && newRate <= 1.0) {
						connection.setPacketLossProbability(newRate);
						tfPacketLossRate.setBackground(Color.WHITE);
					} else {
						tfPacketLossRate.setBackground(Color.RED);
					}
				} catch (Exception exception) {
					tfPacketLossRate.setBackground(Color.RED);
				}
			}
		});


		JLabel lblLabel = new JLabel("Label:");
		lblLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		lblLabel.setBounds(10, 160, 140, 20);
		content.add(lblLabel);
		
		tfLabel = new JTextField();
		tfLabel.setColumns(10);
		tfLabel.setBounds(155, 160, 90, 20);
		content.add(tfLabel);
		tfLabel.getDocument().addDocumentListener(new DocumentListener() {

			@Override
			public void removeUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			@Override
			public void changedUpdate(DocumentEvent e) {
				updateConnectionAndField();
			}

			private void updateConnectionAndField() {
				if(mutex)
					return;
				try {
					short newLabel = Short.parseShort(tfLabel.getText());
					connection.setLabel(newLabel);
					tfLabel.setBackground(Color.WHITE);
				} catch (Exception exception) {
					tfLabel.setBackground(Color.RED);
				}
			}
		});
		
		JLabel lblStatus = new JLabel("Status:");
		lblStatus.setHorizontalAlignment(SwingConstants.RIGHT);
		lblStatus.setBounds(250, 130, 60, 20);
		content.add(lblStatus);

		cmbStatus = new JComboBox<String>();
		for (int i = 0; i < 5; i++)
			cmbStatus.addItem(Connection.getStatusName((byte) i));
		cmbStatus.setBounds(320, 130, 125, 20);

		content.add(cmbStatus);
		cmbStatus.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (mutex) {
					cmbStatus.setSelectedIndex(connection.getStatus());
				}
				if (cmbStatus.getSelectedIndex() < 0 || cmbStatus.getSelectedIndex() > 4)
					cmbStatus.setSelectedIndex(connection.getStatus());
				else
					connection.setStatus((byte) cmbStatus.getSelectedIndex());

			}
		});

		JButton btnCreate = new JButton("Verify and Create");
		btnCreate.setBounds(127, 186, 206, 25);
		content.add(btnCreate);

		btnCreate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				/**
				 * Validate Packet loss rate
				 */
				try{
					double d = Double.parseDouble(tfPacketLossRate.getText());
					if(d < 0.0 && 1.0 < d)
						throw new Exception("Packet loss rate out of bounds");
					connection.setPacketLossProbability(d);
				}catch(Exception e1){
					JOptionPane.showMessageDialog(frame, "Invalid PacketsLossProbability, should be double between 0.0 and 1.0", "Invalid Packetloss Rate",
							JOptionPane.WARNING_MESSAGE);
					return;
				}
				/**
				 * Validate Status
				 */
				if(cmbStatus.getSelectedIndex()<0){
					JOptionPane.showMessageDialog(frame, "Status not set", "Status invalid",
							JOptionPane.WARNING_MESSAGE);
					return;
				}
				connection.setStatus((byte)cmbStatus.getSelectedIndex());
				
				/**
				 * Link should be set and contain connection
				 */
				if (connection.getLink() == null) {
					JOptionPane.showMessageDialog(frame, "Link should not be null.", "Invalid Link Selected",
							JOptionPane.WARNING_MESSAGE);
					return;
				} else if (!connection.getLink().getConnections().contains(connection)){
					network.addConnectionToLink(connection, connection.getLink());
					
				}
				
				if(tfName.getText()!=null)
					connection.setName(tfName.getText());
				
				/**
				 * Check each Port/Device
				 */
				for (int i = 0; i < ports.length; i++) {
					//Add Device to Link
					if (connection.getProtocol().getDevices().contains(ports[i])) {
						/**
						 * If Link part of protocol & connection
						 */
						if (!ports[i].getOwner().getLinks().contains(connection.getLink()))
							network.addLinkToDevice(connection.getLink(), ports[i].getOwner());
						if (!connection.getParticipants().contains(ports[i]))
							connection.addSmartDevice(ports[i]);
						if (ports[i].getConnection() != connection)
							ports[i].setConnection(connection);
						if (!ports[i].getOwner().getPorts().contains(ports[i]))
							ports[i].getOwner().addPort(ports[i]);
					} else {
						/**
						 * Else remove from COnnection
						 */
						network.removeDeviceFromConnectionAndProtocol(ports[i], connection);
						if (ports[i].getOwner().getPorts().contains(ports[i]))
							ports[i].getOwner().removePort(ports[i]);

					}
				}
				
				/**
				 * Connection should be in the model
				 */
				if(!network.getConnections().contains(connection))
					network.addConnection(connection);
				/**
				 * Link should be in the model
				 */
				if(!network.getLinks().contains(connection.getLink())){
					network.addLink(connection.getLink());
				}
				edit = true;
				/**
				 * Close Window
				 */
				controller.notifyObservers();
				content.setVisible(false);
				setVisible(false);
				if (frame != null) {
					frame.setVisible(false);
					frame.dispose();
				}
			}
		});

		/**
		 * Small Label to describe the roles
		 */
		JLabel lblRole = new JLabel("Roles:");
		lblRole.setHorizontalAlignment(SwingConstants.CENTER);
		lblRole.setBounds(345, 189, 50, 18);
		lblRole.setToolTipText("The roles each device plays in the connection, can be defined below. It defines how the device acts.");
		content.add(lblRole);
		
		/**
		 * Height of the current Box which is being created
		 */
		int currentHeight = 220;

		cmbPortRoles = new JComboBox[ports.length];
		for (int i = 0; i < ports.length; i++) {
			// Effectively final variables for the action listener
			int pos = i;
			Port currentPort = ports[i];
			SmartDevice currentDevice = currentPort.getOwner();

			JLabel lblDevice = new JLabel(currentDevice.getName() + ":" + currentPort.getPortNumber() + ":");
			lblDevice.setHorizontalAlignment(SwingConstants.RIGHT);
			lblDevice.setBounds(10, currentHeight, 230, 18);
			content.add(lblDevice);

			cmbPortRoles[pos] = new JComboBox<String>();
			for (String role : connection.getProtocol().getRoles())
				cmbPortRoles[pos].addItem(role);
			cmbPortRoles[pos].addItem("Disconnected");
			cmbPortRoles[pos].setBounds(250, currentHeight, 240, 18);
			content.add(cmbPortRoles[pos]);
			if (edit) {
				int roleOfDevice = connection.getProtocol().getRoleOfDevice(currentPort);
				if (roleOfDevice != -1)
					cmbPortRoles[pos].setSelectedIndex(roleOfDevice);
				else
					cmbPortRoles[pos].setSelectedIndex(disconnectedIndex);
			} else {
				/**
				 * Number of tries to add the Port
				 */
				int n = 5;
				for (int j = 0; j <= n; j++) {
					if (j == 0) {// Try to add Router first
						if (network.addDeviceToConnectionAndProtocol(currentPort, connection, 0)) {
							cmbPortRoles[pos].setSelectedIndex(0);
							break;
						}
					} else if (j == n) {// If it could not be added
						cmbPortRoles[pos].setSelectedIndex(disconnectedIndex);
						network.removeDeviceFromConnectionAndProtocol(currentPort, connection);
						break;
					} else {
						int randomRole = ThreadLocalRandom.current().nextInt(0, disconnectedIndex);
						if (network.addDeviceToConnectionAndProtocol(currentPort, connection, randomRole)) {
							cmbPortRoles[pos].setSelectedIndex(randomRole);
							break;
						}
					}
				}
			}
			cmbPortRoles[pos].addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					if (protocolChange) {
						return;
					}
					/**
					 * Selected Index of the ComboBox, which represents the new
					 * role (or disconnected)
					 */
					int selected = cmbPortRoles[pos].getSelectedIndex();
					/**
					 * True if role was successfully changed
					 */
					boolean successfullChange = network.changeRoleOfDevice(connection.getProtocol(), connection,
							ports[pos], selected);
					/**
					 * Set to Disconnected, if role could not be changed
					 */
					if (!successfullChange) {
						cmbPortRoles[pos].setSelectedIndex(connection.getProtocol().getNumberOfRoles());
					}
				}
			});
			currentHeight += 20;
		}
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				if(!edit){
					if(newLink){
						network.deleteLink(connection.getLink());
					}
					network.deleteConnection(connection);
				}
			};
		});
		refreshGUI();
	}

	/**
	 * Refreshes the GUI
	 */
	private void refreshGUI() {
		// Set Mutex
		mutex = true;
		/*
		 * Update Variables
		 */

		disconnectedIndex = connection.getProtocol().getNumberOfRoles();

		availableProtocols = controller.getImportController().getProtocols();
		availableConnection = controller.getImportController().getConnections();

		/**
		 * Update Boxes, Textfield e.g.
		 */

		// Update Name Textfield
		tfName.setText(connection.getName());

		// Update packet loss field
		tfPacketLossRate.setText("" + connection.getPacketLossProbability());

		// Update Label
		tfLabel.setText("" + connection.getLabel());
		
		// Update Status
		cmbStatus.setSelectedIndex(connection.getStatus());

		// Update selected Connection
		cmbConnection.removeAllItems();
		for (Class<? extends Connection> c : availableConnection)
			cmbConnection.addItem(c.getSimpleName());
		// Set Index to selected Connection
		for (int i = 0; i < availableConnection.size(); i++)
			if (connection.getClass().equals(availableConnection.get(i))) {
				// Select the right protocol and save last index
				lastConnectionIndex = i;
			}
		cmbConnection.setSelectedIndex(lastConnectionIndex);

		// Update selected Link
		int linkIndex = 0;
		for (Link l : network.getLinks()) {
			cmbSelectedLink.addItem(l.getName());
			if (l.equals(connection.getLink())) {
				lastLinkIndex = linkIndex;
			}
			linkIndex++;
		}
		cmbSelectedLink.setSelectedIndex(lastLinkIndex);

		// Update protocol
		cmbProtocolType.removeAllItems();
		for (int i = 0; i < availableProtocols.size(); i++)
			try {
				cmbProtocolType.addItem(availableProtocols.get(i).newInstance().getName());
			} catch (InstantiationException | IllegalAccessException e1) {
				System.out.println("Protocol " + i + " is invalid");
				cmbProtocolType.addItem("unknown");
			}
		// Set Index to selected Protocol
		lastProtocolIndex = -1;
		for (int i = 0; i < availableProtocols.size(); i++) {
			if (connection.getProtocol().getClass().equals(availableProtocols.get(i))) {
				// Select the right protocol and save last index
				lastProtocolIndex = i;
			}
		}
		cmbProtocolType.setSelectedIndex(lastProtocolIndex);

		// Release mutex
		mutex = false;
	}

	/**
	 * Test the Panel
	 */
	private static void testGUI() {
		JFrame frame = new JFrame("Connection Creation Panel");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		ConnectionCreationPanel panel;
		Link testNet = new SimpleLink("Test Network");
		LinkedList<SmartDevice> devicesOfLink = new LinkedList<SmartDevice>();
		LinkedList<Port> ports = new LinkedList<Port>();
		for (int i = 0; i < 5; i++) {
			SmartDevice d = new SmartDevice("Device" + i);
			Port p = new Port(d, (short) i);
			d.addPort(p);
			devicesOfLink.add(d);
			ports.add(p);
		}
		Model m = new Model();
		m.addConnectionNetwork(testNet);
		m.addConnectionNetwork(new SimpleLink("Wifi"));
		m.addConnectionNetwork(new SimpleLink("Ethernet"));
		panel = new ConnectionCreationPanel(ports, testNet, new Controller(m),frame);
		frame.setContentPane(panel);
		frame.pack();
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				testGUI();
			}
		});
	}
}