public class AndroidDeviceClient implements Serializable {

	private static final long serialVersionUID = -4688390733043132636L;

	/**
	 * The device serial number
	 */
	private final String serialNumber;

	/**
	 * The device model
	 */
	private final String model;

	/**
	 * The device client id
	 */
	private String client;

	private boolean online = true;

	public AndroidDeviceClient(String serialNumber, String model) {
		this.model = model;
		this.serialNumber = serialNumber;
	}

	/**
	 * Get device serial number
	 * 
	 * @return the device serial number
	 */
	public String getSerialNumber() {
		return serialNumber;
	}

	/**
	 * The device model
	 * 
	 * @return the device model (pretty string to show to users)
	 */
	public String getModel() {
		return model;
	}

	/**
	 * This device online state
	 * 
	 * @return true if the device is online, false otherwise
	 */
	public boolean isOnline() {
		return online;
	}

	/**
	 * set this device online state. For now it has the same online definition
	 * as ddms
	 * 
	 * @param online
	 *            true if online, false otherwise
	 */
	public void setOnline(boolean online) {
		this.online = online;
	}

	/**
	 * Set this device client address
	 * 
	 * @param client
	 *            the client id
	 */
	public void setClient(String client) {
		this.client = client;
	}

	/**
	 * Get this device client id
	 * 
	 * @return the client id
	 */
	public String getClient() {
		return client;
	}

	@Override
	public boolean equals(Object obj) {
		boolean equals = false;
		if (obj instanceof AndroidDeviceClient) {
			AndroidDeviceClient deviceClient = (AndroidDeviceClient) obj;
			if (deviceClient.getModel().equals(model)
					&& deviceClient.getSerialNumber().equals(serialNumber)) {
				equals = true;
			}

		}
		return equals;
	}

	@Override
	public String toString() {
		return model + "(" + serialNumber + ")";
	}

} public class Client {

	private static final String DEFAULT_LOGCAT_PATH = RemoteResourcesConfiguration
			.getRemoteResourcesDir() != null ? RemoteResourcesConfiguration
			.getRemoteResourcesDir() + File.separator + "logs" : System
			.getProperty("user.home");

	private File logFilePath;

	private String logFileName;

	private Socket connection;

	private final InetAddress address;

	private final String hostname;

	private final int port;

	private ObjectOutputStream outputStream;

	private ObjectInputStream inputStream;

	private final Map<String, AndroidDeviceClient> devices = new HashMap<String, AndroidDeviceClient>();

	private final AtomicInteger sequenceNumber = new AtomicInteger();

	private final Map<Integer, Message> messages = new HashMap<Integer, Message>();

	private final Map<String, DataReceivedListener> dataListeners = new HashMap<String, DataReceivedListener>();

	private final Map<String, DataClient> dataConnections = new HashMap<String, DataClient>();

	private final ClientMessageHandler messageHandler;

	private final Status status = Status.OK;

	private DataReceivedListener sshotListener;

	private ClientDeviceChangesThread deviceChangesThread;

	private static final String LOG_FILE_EXTENSION = ".txt";

	public Client(String hostname, int port) throws UnknownHostException {
		InetAddress address = InetAddress.getByName(hostname);
		this.hostname = hostname;
		this.address = address;
		this.port = port != 0 ? port
				: IConnectionConstants.CONTROL_SERVER_DEFAULT_PORT;
		messageHandler = new ClientMessageHandler(this);
	}

	void connect() throws IOException {
		if (connection == null) {
			ClientLogger
					.info(Client.class,
							RemoteResourcesLocalization
									.getMessage(
											RemoteResourcesMessages.Client_Info_ConnectingToServer,
											new Object[] { hostname, port }),
							null);
			connection = new Socket(address, port);
			outputStream = new ObjectOutputStream(connection.getOutputStream());
			inputStream = new ObjectInputStream(connection.getInputStream());
			ClientInputHandler handler = new ClientInputHandler();
			handler.start();
			listDevices();
		}
	}

	void disconnect() {

		ClientLogger.info(Client.class, RemoteResourcesLocalization.getMessage(
				RemoteResourcesMessages.Client_Info_DisconnectingFromServer,
				new Object[] { hostname, port }), null);

		deviceChangesThread.cancel();
		for (String dataConnection : dataConnections.keySet()) {
			ClientConnectionManager.getInstance().stopDataConnection(this,
					dataConnection);
		}
		if (outputStream != null) {
			try {
				outputStream.close();
			} catch (IOException e) {
				// do nothing
			}
		}
		if (inputStream != null) {
			try {
				inputStream.close();
			} catch (IOException e) {
				// do nothing
			}
		}
		if (connection != null) {
			try {
				connection.close();
			} catch (IOException e) {
				// do nothing
			}
		}
		connection = null;
		outputStream = null;
		inputStream = null;
	}

	/**
	 * Return this client device list
	 * 
	 * @return a copy of the list of devices of this client
	 */
	public synchronized Map<String, AndroidDeviceClient> getDeviceList() {
		return devices;
	}

	/**
	 * Add a new device. Only intended to be used by message handler
	 * 
	 * @param device
	 *            the device
	 */
	void addDevice(AndroidDeviceClient device) {
		device.setClient(getDisplayName());
		if (devices.get(device.getSerialNumber()) == null) {
			devices.put(device.getSerialNumber(), device);
		}
	}

	void removeDevice(AndroidDeviceClient device) {
		if (isDataConnectionActive(device.getSerialNumber())) {
			ClientConnectionManager.getInstance().stopDataConnection(this,
					device.getSerialNumber());
		}
		devices.remove(device.getSerialNumber());

	}

	/**
	 * dispatch a get device list message to the server
	 */
	private void listDevices() {
		sendMessage(new GetDeviceListMessage(getNextSequenceNumber(),
				GetDeviceListMessageType.ALL));
	}

	/**
	 * Get a message from message pool. Only intended to be used by message
	 * handler;
	 * 
	 * @param sequenceNumber
	 *            the message sequence number
	 * @return the message with the desired sequence number or null with we have
	 *         no message
	 */
	Message getMessage(Integer sequenceNumber) {
		return messages.get(sequenceNumber);
	}

	/**
	 * Discard a message with a desired sequence number
	 * 
	 * @param sequenceNumber
	 *            the sequence number
	 */
	void discardMessage(Integer sequenceNumber) {
		messages.remove(sequenceNumber);
	}

	/**
	 * Send a message to the server associated with this client
	 * 
	 * @param message
	 *            the message
	 */
	public void sendMessage(Message message) {
		messages.put(message.getSequenceNumber(), message);
		try {
			outputStream.writeObject(message);
		} catch (IOException e) {
			ClientLogger
					.error(Client.class,
							RemoteResourcesLocalization
									.getMessage(
											RemoteResourcesMessages.Client_Error_UnableToSendMessage,
											new Object[] { hostname, port }), e);
			disconnect();
		} catch (NullPointerException e) {
			disconnect();
			try {
				connect();
				outputStream.writeObject(message);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}

	}

	public int getNextSequenceNumber() {
		return sequenceNumber.getAndAdd(1);
	}

	/**
	 * Adds a new data received listener for a certain device
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 * @param dataReceivedListener
	 *            the listener
	 */
	public void addDataReceivedListener(String deviceSerialNumber,
			DataReceivedListener dataReceivedListener) {
		dataListeners.put(deviceSerialNumber, dataReceivedListener);
	}

	/**
	 * Remove a data received listener for a certain device
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 * @param dataReceivedListener
	 *            the listener to be removed
	 */
	public void removeDataReceivedListener(String deviceSerialNumber,
			DataReceivedListener dataReceivedListener) {
		dataListeners.remove(deviceSerialNumber);
	}

	/**
	 * Notify listeners about new data
	 * 
	 * @param serialNumber
	 *            the device serial number
	 * @param data
	 *            the received data
	 */
	void notifyDataReceived(String serialNumber, byte[] data) {
		DataReceivedListener listener = dataListeners.get(serialNumber);
		if (listener != null) {
			listener.newFrameReceived(serialNumber, data);
		}

	}

	/**
	 * Requests a new data connection with the device with the matching serial
	 * number. Does nothing if a connection is already done (in this case just
	 * register a new data listener)
	 * 
	 * Use the method {@link Client#isDataConnectionActive(String)} to check if
	 * the connection is already set
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 */
	void startDataConnection(String deviceSerialNumber) {
		if (!isDataConnectionActive(deviceSerialNumber)) {
			sendMessage(new SetupDeviceMessage(getNextSequenceNumber(),
					SetupDeviceMessageType.CONNECT_DEVICE, deviceSerialNumber,
					null));
		}
	}

	/**
	 * Stop device data connection.
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 */
	void stopDataConnection(String deviceSerialNumber) {
		if (isDataConnectionActive(deviceSerialNumber)) {
			dataConnections.remove(deviceSerialNumber).close();
			sendMessage(new SetupDeviceMessage(getNextSequenceNumber(),
					SetupDeviceMessageType.DISCONNECT_DEVICE,
					deviceSerialNumber, null));
		}
	}

	/**
	 * Connects the device data channel
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 * @param port
	 *            data connection port. The address will be the same as the
	 *            client
	 */
	void connectDeviceDataChannel(String deviceSerialNumber, Integer port) {
		DataClient dataClient = new DataClient(deviceSerialNumber, this,
				new InetSocketAddress(hostname, port));
		dataClient.start();
		dataConnections.put(deviceSerialNumber, dataClient);
	}

	/**
	 * Check if a data connection for a requested serial number is already in
	 * place
	 * 
	 * @param deviceSerialNumber
	 *            the device serial number
	 * @return true if data connection is active, false otherwise
	 */
	public boolean isDataConnectionActive(String deviceSerialNumber) {
		return dataConnections.get(deviceSerialNumber) != null;
	}

	/**
	 * Check if there is any data connection.
	 * 
	 * @return true if there is a data connection active, false otherwise
	 * */
	public boolean isAnyDataConnectionActive() {
		return !dataConnections.entrySet().isEmpty();
	}

	/**
	 * This class handles incoming messages from server side. This class is a
	 * thread so it could be ran in parallel.
	 * 
	 * @author Marcelo Marzola Bossoni
	 * 
	 */
	class ClientInputHandler extends Thread {

		@Override
		public void run() {
			// just read the next message in the stream and call the
			// appropriated handler
			try {
				Message message = (Message) inputStream.readObject();
				while (message != null) {
					messageHandler.handleMessage(message);
					message = (Message) inputStream.readObject();
				}
			} catch (ClassNotFoundException e) {
				ClientLogger
						.error(ClientInputHandler.class,
								RemoteResourcesLocalization
										.getMessage(
												RemoteResourcesMessages.Client_Error_UnknownObjectType,
												new Object[] { hostname, port }),
								e);
				disconnect();
			} catch (IOException e) {
				ClientLogger.error(ClientInputHandler.class,
						RemoteResourcesLocalization.getMessage(
								RemoteResourcesMessages.Client_Error_IOError,
								new Object[] { hostname, port }), e);
				disconnect();
			}
		}

	}

	class ClientDeviceChangesThread extends Thread {
		private boolean cancel = false;

		@Override
		public void run() {
			while (!cancel) {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					// do nothing
				}
				sendMessage(new GetDeviceListMessage(getNextSequenceNumber(),
						GetDeviceListMessageType.REMOVED));
				sendMessage(new GetDeviceListMessage(getNextSequenceNumber(),
						GetDeviceListMessageType.ADDED));
			}
		}

		public synchronized void cancel() {
			cancel = true;
		}

	}

	public synchronized boolean isConnected() {
		return deviceChangesThread != null;
	}

	synchronized void setConnected() {
		if (deviceChangesThread == null) {
			deviceChangesThread = new ClientDeviceChangesThread();
			deviceChangesThread.start();
		}
	}

	public int getPort() {
		return port;
	}

	public String getHostname() {
		return hostname;
	}

	/**
	 * Get a textual representation of this client name
	 * 
	 * @return the string representing this client name
	 */
	public String getDisplayName() {
		return hostname + ":" + port; //$NON-NLS-1$
	}

	/**
	 * Return this client status. Errors are expected on client connection
	 * errors
	 * 
	 * @return the actual client status
	 */
	public Status getStatus() {
		return status;
	}

	/**
	 * Set the time between two screen captures
	 * 
	 * @param serialNumber
	 *            the device serial number
	 * @param value
	 *            the time (in ms) to wait before the next screen capture
	 */
	public void setPollingRate(String serialNumber, String value) {
		if (isDataConnectionActive(serialNumber)) {
			try {
				sendMessage(new SetupDeviceMessage(getNextSequenceNumber(),
						SetupDeviceMessageType.SET_POLLING_RATE, serialNumber,
						Long.parseLong(value)));
			} catch (NumberFormatException e) {
				ClientLogger.warning(Client.class,
						"Unable to set polling rate for device", e);
			}
		}
	}

	/**
	 * Set the image quality
	 * 
	 * @param serialNumber
	 *            the device serial number
	 * @param value
	 *            the value, between 0 and 100, of image quality
	 */
	public void setImageQuality(String serialNumber, String value) {
		if (isDataConnectionActive(serialNumber)) {
			try {
				sendMessage(new SetupDeviceMessage(getNextSequenceNumber(),
						SetupDeviceMessageType.SET_IMAGE_QUALITY, serialNumber,
						Integer.parseInt(value)));
			} catch (NumberFormatException e) {
				ClientLogger.warning(Client.class,
						"Unable to set image quality for device", e);
			}
		}
	}

	public void takeScreenshot(String serialNumber,
			DataReceivedListener listener) {
		sshotListener = listener;
		sendMessage(new GetNewFrameMessage(getNextSequenceNumber(),
				serialNumber, 75));
	}

	void saveScreenshot(String serialNumber, byte[] sshot) {
		sshotListener.newFrameReceived(serialNumber, sshot);
		sshotListener = null;
	}

	public String generateNameFileLog(String device) {

		String date_mask = "yyyyMMdd";

		String device_nickname = "LogFile_";
		if (DeviceUtils.getInstance().getDeviceNickname(device) != null) {
			device_nickname += DeviceUtils.getInstance().getDeviceNickname(
					device)
					+ "_";
		}

		java.util.Date today = new java.util.Date();
		Calendar cal = Calendar.getInstance();
		cal.setTime(today);
		SimpleDateFormat format_date = new SimpleDateFormat(date_mask);

		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(device_nickname);
		stringBuilder.append(getHostname());
		stringBuilder.append("_");
		stringBuilder.append(String.valueOf(getPort()));
		stringBuilder.append("_");
		stringBuilder.append(format_date.format(today));
		stringBuilder.append("_");
		stringBuilder.append(cal.get(Calendar.HOUR_OF_DAY));
		stringBuilder.append(cal.get(Calendar.MINUTE));
		stringBuilder.append(cal.get(Calendar.SECOND));
		String logfile_name = stringBuilder.toString();

		return logfile_name + LOG_FILE_EXTENSION;
	}

	public String generateNameFileLog() {
		return generateNameFileLog("");
	}

	public void createFileLog(String deviceSerialNumber, String data) {

		try {
			String outputFile;
			if (logFilePath == null) {
				outputFile = DEFAULT_LOGCAT_PATH + File.separator + logFileName;
			} else {
				outputFile = logFilePath.getAbsolutePath() + File.separator
						+ logFileName;
			}

			FileWriter fw = new FileWriter(outputFile, true);
			fw.write(data);
			fw.close();
		} catch (IOException e) {
			// TODO: log exception
		}

	}

	public String getDefaultLogcatPath() {
		return DEFAULT_LOGCAT_PATH;
	}

	/**
	 * Handle the logcat Capture
	 * 
	 * @param serialNumber
	 *            the device serial number
	 * @param file
	 *            complete name of the log to be saved
	 */
	public void getLogCatCapture(String deviceSerialNumber, File file,
			String name, ControlLogCaptureTypeMessage level) {
		if (isDataConnectionActive(deviceSerialNumber)) {
			logFilePath = file;
			logFileName = name;
			sendMessage(new ControlLogCaptureMessage(getNextSequenceNumber(),
					deviceSerialNumber, level));
		}
	}

	/*
	 * public void handleFile(String deviceSerialNumber, String localFile,
	 * String remoteFile, ControlFileHandlingTypeMessage handlingType) { if
	 * (isDataConnectionActive(deviceSerialNumber)) { sendMessage(new
	 * ControlFileHandlingMessage(getNextSequenceNumber(), deviceSerialNumber,
	 * localFile, remoteFile, handlingType)); } }
	 * 
	 * public void runShellCommand(String deviceSerialNumber, String command) {
	 * if (isDataConnectionActive(deviceSerialNumber)) { sendMessage(new
	 * ControlRunShellCommandMessage( getNextSequenceNumber(),
	 * deviceSerialNumber, command)); } }
	 */

	public void runCommand(String deviceSerialNumber, Command command) {
		if (isDataConnectionActive(deviceSerialNumber)) {
			Message m = CommandMessageFactory.getCommandMessage(command,
					getNextSequenceNumber(), deviceSerialNumber);
			sendMessage(m);
		}
	}

	// TODO Handle the adb shell response. Needs some UI to display the results
	public void displayRunShellCommandResult(String serialNumber, String data) {
		ClientLogger.debug(ControlRunShellCommandMessage.class, data, null);

		JOptionPane.showMessageDialog(null, data, "ADB command result",
				JOptionPane.ERROR_MESSAGE);
	}

	// TODO Handle the file handling response. Needs some UI to display the
	// results
	public void displayFileHandlingResult(String serialNumber, String data) {
		ClientLogger.debug(ControlRunShellCommandMessage.class, data, null);

		JOptionPane.showMessageDialog(null, data, "File handling result",
				JOptionPane.ERROR_MESSAGE);
	}

	// TODO Handle the package handling response. Needs some UI to display the
	// results
	public void displayPackageHandlingResult(String serialNumber, String data) {
		ClientLogger.debug(ControlPackageHandlingMessage.class, data, null);

		JOptionPane.showMessageDialog(null, data, "Package handling result",
				JOptionPane.ERROR_MESSAGE);
	}

	// TODO Handle the reboot response. Needs some UI to display the
	// results
	public void displayRebootCommandResult(String serialNumber, String data) {
		ClientLogger.debug(ControlPackageHandlingMessage.class, data, null);

		JOptionPane.showMessageDialog(null, data, "Reboot result",
				JOptionPane.ERROR_MESSAGE);
	}
} public class ClientConnectionManager {

	public static final String LOCALHOST = IConnectionConstants.LOCALHOST;

	public static final int LOCALPORT = RemoteResourcesConfiguration
			.getInstance().getServerPort();

	private static ClientConnectionManager instance;

	private final Map<String, Client> clients = new HashMap<String, Client>();

	private final Set<ClientChangedListener> listeners;

	/**
	 * We currently support only one active device connected
	 */
	private Client activeClient = null;

	private String activeDevice = null;

	private ClientConnectionManager() {
		listeners = new HashSet<ClientChangedListener>();
	}

	/**
	 * Add a client changed listener. Has no effect if listener is already
	 * registered
	 * 
	 * @param listener
	 *            the listener
	 */
	public void addClientChangedListener(ClientChangedListener listener) {
		listeners.add(listener);
	}

	/**
	 * Remove a client changed listener
	 * 
	 * @param listener
	 *            the listener
	 */
	public void removeClientChangedListener(ClientChangedListener listener) {
		listeners.remove(listener);
	}

	public static ClientConnectionManager getInstance() {
		if (instance == null) {
			instance = new ClientConnectionManager();
		}
		return instance;
	}

	/**
	 * Connect to a server
	 * 
	 * @param hostName
	 *            the address to connect to
	 * @param port
	 *            the port
	 * @return the client connection
	 * @throws IOException
	 *             if no connection was possible
	 * @throws UnknownHostException
	 *             if was not possible to resolve host
	 */
	public Client connect(String hostName, int port)
			throws UnknownHostException, IOException {
		final Client client = new Client(hostName, port);
		client.connect();
		new Thread("Check device state") { //$NON-NLS-1$
			@Override
			public void run() {
				while (!client.isConnected()
						&& (client.getStatus() == Status.OK)) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// do nothing
					}
				}
				if (client.getStatus() == Status.OK) {
					List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
					copy.addAll(listeners);
					for (ClientChangedListener listener : copy) {
						listener.clientConnected(client);
					}
				}
			};
		}.start();
		clients.put(hostName + ":" + port, client); //$NON-NLS-1$
		return client;
	}

	private void removeClient(String hostname, int port) {
		clients.remove(hostname + ":" + port); //$NON-NLS-1$
	}

	public void disconnect(String hostname, int port) {
		Client client = getClient(hostname, port);
		if (client != null) {
			removeClient(hostname, port);
			disconnect(client);
		}
		List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
		copy.addAll(listeners);
		for (ClientChangedListener listener : copy) {
			listener.clientDisconnected(client);
		}
	}

	public void disconnect(Client client) {
		client.disconnect();
	}

	public Client getClient(String hostname, int port) {
		return clients.get(hostname + ":" + port); //$NON-NLS-1$
	}

	public Client getClient(String id) {
		return clients.get(id);
	}

	public void addDevice(Client c, AndroidDeviceClient device) {
		if (c.getDeviceList().get(device.getSerialNumber()) == null) {
			c.addDevice(device);
			List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
			copy.addAll(listeners);
			for (ClientChangedListener listener : copy) {
				listener.deviceAdded(c, device.getSerialNumber());
			}
		}
	}

	public void removeDevice(Client c, AndroidDeviceClient device) {
		c.removeDevice(device);
		List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
		copy.addAll(listeners);
		for (ClientChangedListener listener : copy) {
			listener.deviceRemoved(c, device);
		}
	}

	public void startDataConnection(Client c, String device) {
		if ((activeClient != null) && (activeDevice != null)) {
			stopDataConnection(activeClient, activeDevice);
		}
		c.startDataConnection(device);
		activeClient = c;
		activeDevice = device;
		List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
		copy.addAll(listeners);
		for (ClientChangedListener listener : copy) {
			listener.deviceConnected(c, device);
		}
	}

	public void stopDataConnection(Client c, String device) {
		if (c.isDataConnectionActive(device)) {
			c.stopDataConnection(device);
			List<ClientChangedListener> copy = new ArrayList<ClientChangedListener>();
			copy.addAll(listeners);
			for (ClientChangedListener listener : copy) {
				listener.deviceDisconnected(c, device);
			}
		}
	}

	/**
	 * Close this manager
	 */
	public void close() {
		// do nothing
	}

} public class RemoteResourcesLocalization {

	public enum RemoteResourcesLocalization_Languages {
		en_US, pt_BR
	};

	private static ResourceBundle bundle = ResourceBundle.getBundle(
			"com.eldorado.remoteresources.i18n.RemoteResourcesMessages",
			Locale.getDefault());

	/**
	 * Get a message with a following id
	 * 
	 * @param message
	 *            the message id
	 * @return the message
	 */
	public static String getMessage(String message) {
		return bundle.getString(message);
	}

	/**
	 * Get a message with the parameters in place
	 * 
	 * @param message
	 *            the message id
	 * @param parameters
	 *            the parameters
	 * @return the compound message
	 */
	public static String getMessage(String message, Object... parameters) {
		return MessageFormat.format(bundle.getString(message), parameters);
	}

	public static void setLanguage(
			RemoteResourcesLocalization_Languages language) {
		switch (language) {
		case en_US:
			bundle = ResourceBundle
					.getBundle(
							"com.eldorado.remoteresources.i18n.RemoteResourcesMessages",
							Locale.getDefault());
			break;
		case pt_BR:
			bundle = ResourceBundle
					.getBundle(
							"com.eldorado.remoteresources.i18n.RemoteResourcesMessages",
							new Locale("pt", "BR"));
			break;
		default:
			bundle = ResourceBundle
					.getBundle(
							"com.eldorado.remoteresources.i18n.RemoteResourcesMessages",
							Locale.getDefault());
		}
	}
} public class RemoteResourcesMessages {

	/**
	 * Add new messages here in the following format: Class_Category_MessageName
	 * Where Category can be Info, Error, Warning, Debug (for log information)
	 * or UI (for ui labels and messages)
	 */

	/*
	 * RemoteResources
	 */
	public static final String RemoteResources_OneInstance = "RemoteResources_OneInstance";

	/*
	 * AddDeviceAction
	 */
	public static final String AddDeviceAction_Tooltip_AddDevice = "AddDeviceAction_Tooltip_AddDevice";

	public static final String AddDeviceAction_LongTooltip_AddDevice = "AddDeviceAction_LongTooltip_AddDevice";

	/*
	 * AddDevicePage
	 */
	public static final String AddDevicePage_Error_DeviceAlreadyExists = "AddDevicePage_Error_DeviceAlreadyExists";

	public static final String AddDevicePage_Error_DeviceAlreadyCreated = "AddDevicePage_Error_DeviceAlreadyCreated";

	public static final String AddDevicePage_Error_NameIsEmpty = "AddDevicePage_Error_NameIsEmpty";

	public static final String AddDevicePage_Error_NoAvailableDevice = "AddDevicePage_Error_NoAvailableDevice";

	public static final String AddDevicePage_Error_NoSelectedDevice = "AddDevicePage_Error_NoSelectedDevice";

	public static final String AddDevicePage_UI_DeviceTableColumn_DeviceModel = "AddDevicePage_UI_DeviceTableColumn_DeviceModel";

	public static final String AddDevicePage_UI_DeviceTableColumn_DeviceSerialNumber = "AddDevicePage_UI_DeviceTableColumn_DeviceSerialNumber";

	public static final String AddDevicePage_UI_DeviceNameLabel = "AddDevicePage_UI_DeviceNameLabel";

	public static final String AddDevicePage_UI_DeviceNameTooltip = "AddDevicePage_UI_DeviceNameTooltip";

	public static final String AddDevicePage_UI_PageDescription = "AddDevicePage_UI_PageDescription";

	/*
	 * AddHostPage
	 */

	public static final String AddHostPage_Error_EmptyHostname = "AddHostPage_Error_EmptyHostname";

	public static final String AddHostPage_Error_ExistingHostNickname = "AddHostPage_Error_ExistingHostNickname";

	public static final String AddHostPage_Error_InvalidIPAddress = "AddHostPage_Error_InvalidIPAddress";

	public static final String AddHostPage_Error_InvalidPort = "AddHostPage_Error_InvalidPort";

	public static final String AddHostPage_Error_UnableToConnect = "AddHostPage_Error_UnableToConnect";

	public static final String AddHostPage_Error_UnknownHost = "AddHostPage_Error_UnknownHost";

	public static final String AddHostPage_UI_HostsComboLabel = "AddHostPage_UI_HostsComboLabel";

	public static final String AddHostPage_UI_NewHost_HostAddress = "AddHostPage_UI_NewHost_HostAddress";

	public static final String AddHostPage_UI_NewHost_HostNickname = "AddHostPage_UI_NewHost_HostNickname";

	public static final String AddHostPage_UI_NewHost_HostPort = "AddHostPage_UI_NewHost_HostPort";

	public static final String AddHostPage_UI_PageDescription = "AddHostPage_UI_PageDescription";

	public static final String AddHostPage_UI_Radio_ExistingHost_Label = "AddHostPage_UI_Radio_ExistingHost_Label";

	public static final String AddHostPage_UI_Radio_NewHost_Label = "AddHostPage_UI_Radio_NewHost_Label";

	public static final String AddHostPage_Warning_NoHostValidationPerformed = "AddHostPage_Warning_NoHostValidationPerformed";

	/*
	 * ButtonBarPanel
	 */
	public static final String ButtonBarPanel_TypeMessage = "ButtonBarPanel_TypeMessage";
	public static final String ButtonBarPanel_ActionsMenu = "ButtonBarPanel_ActionsMenu";

	/*
	 * Client
	 */
	public static final String Client_Error_IOError = "Client_Error_IOError";

	public static final String Client_Error_UnableToSendMessage = "Client_Error_UnableToSendMessage";

	public static final String Client_Error_UnknownObjectType = "Client_Error_UnknownObjectType";

	public static final String Client_Info_ConnectingToServer = "Client_Info_ConnectingToServer";

	public static final String Client_Info_DisconnectingFromServer = "Client_Info_DisconnectingFromServer";

	/*
	 * ClientLogger
	 */
	public static final String ClientLogger_Error_InsufficientPermissions = "ClientLogger_Error_InsufficientPermissions";

	public static final String ClientLogger_Error_UnableToConfigure = "ClientLogger_Error_UnableToConfigure";

	/*
	 * ConnectDeviceAction
	 */
	public static final String ConnectDeviceAction_Tooltip_StartCapture = "ConnectDeviceAction_Tooltip_StartCapture";

	public static final String ConnectDeviceAction_Tooltip_StopCapture = "ConnectDeviceAction_Tooltip_StopCapture";

	public static final String ConnectDeviceAction_CaptureErrorMessage = "ConnectDeviceAction_CaptureErrorMessage";

	public static final String ConnectDeviceAction_ChangeDevice = "ConnectDeviceAction_ChangeDevice";

	public static final String ConnectDeviceAction_RemoveDevice = "ConnectDeviceAction_RemoveDevice";

	public static final String ConnectDeviceAction_RemoveDeviceTitle = "ConnectDeviceAction_RemoveDeviceTitle";

	public static final String ConnectDeviceAction_RemoveDeviceYes = "ConnectDeviceAction_RemoveDeviceYes";

	public static final String ConnectDeviceAction_RemoveDeviceNo = "ConnectDeviceAction_RemoveDeviceNo";

	/*
	 * ConnectHostAction
	 */
	public static final String ConnectHostAction_Tooltip_StartHost = "ConnectHostAction_Tooltip_StartHost";

	public static final String ConnectHostAction_Tooltip_StopHost = "ConnectHostAction_Tooltip_StopHost";

	/*
	 * DataClient
	 */
	public static final String DataClient_Error_IOError = "DataClient_Error_IOError";

	public static final String DataClient_Error_UnknownObjectType = "DataClient_Error_UnknownObjectType";

	public static final String DataClient_Error_UnableToCloseProperly = "DataClient_Error_UnableToCloseProperly";

	public static final String DataClient_UI_ThreadName = "DataClient_UI_ThreadName";

	/*
	 * DevicePanel
	 */
	public static final String DevicePanel_Tooltip_Configuration = "DevicePanel_Tooltip_Configuration";

	public static final String DevicePanel_Tooltip_Remotion = "DevicePanel_Tooltip_Remotion";

	public static final String DevicePanel_Tooltip_StartCapture = "DevicePanel_Tooltip_StartCapture";

	/*
	 * DeviceConfigurationDialog
	 */
	public static final String DeviceConfigurationDialog_EditNicknameButton = "DeviceConfigurationDialog_EditNicknameButton";

	public static final String DeviceConfigurationDialog_ScriptLocation = "DeviceConfigurationDialog_ScriptLocation";

	public static final String DeviceConfigurationDialog_ActivateScriptGeneration = "DeviceConfigurationDialog_ActivateScriptGeneration";

	public static final String DeviceConfigurationDialog_PollingLabel = "DeviceConfigurationDialog_PollingLabel";

	public static final String DeviceConfigurationDialog_QualityLabel = "DeviceConfigurationDialog_QualityLabel";

	public static final String DeviceConfigurationDialog_DeviceHost = "DeviceConfigurationDialog_DeviceHost";

	public static final String DeviceConfigurationDialog_DeviceName = "DeviceConfigurationDialog_DeviceName";

	public static final String DeviceConfigurationDialog_DeviceSerialNumber = "DeviceConfigurationDialog_DeviceSerialNumber";

	public static final String DeviceConfigurationDialog_Title = "DeviceConfigurationDialog_Title";

	public static final String DeviceConfigurationDialog_EditNickNameError = "DeviceConfigurationDialog_EditNickNameError";

	/*
	 * HostPanel
	 */
	public static final String HostPanel_Tooltip_Remotion = "HostPanel_Tooltip_Remotion";

	public static final String HostPanel_Tooltip_Information = "HostPanel_Tooltip_Information";

	public static final String HostPanel_RemoveHost = "HostPanel_RemoveHost";

	/*
	 * HostConfigurationDialog
	 */
	public static final String HostConfigurationDialog_DeviceList = "HostConfigurationDialog_DeviceList";

	public static final String HostConfigurationDialog_NoDeviceMessage = "HostConfigurationDialog_NoDeviceMessage";

	public static final String HostConfigurationDialog_HostNickname = "HostConfigurationDialog_HostNickname";

	public static final String HostConfigurationDialog_HostAddress = "HostConfigurationDialog_HostAddress";

	public static final String HostConfigurationDialog_HostPort = "HostConfigurationDialog_HostPort";

	public static final String HostConfigurationDialog_Title = "HostConfigurationDialog_Title";

	/*
	 * IConnnectionConstants
	 */

	public static final String IConnnectionConstants_PollingRates1 = "IConnnectionConstants_PollingRates1";

	public static final String IConnnectionConstants_PollingRates2 = "IConnnectionConstants_PollingRates2";

	public static final String IConnnectionConstants_PollingRates3 = "IConnnectionConstants_PollingRates3";

	public static final String IConnnectionConstants_PollingRates4 = "IConnnectionConstants_PollingRates4";

	public static final String IConnnectionConstants_PollingRates5 = "IConnnectionConstants_PollingRates5";

	public static final String IConnnectionConstants_PollingRates6 = "IConnnectionConstants_PollingRates6";

	public static final String IConnnectionConstants_Qualities1 = "IConnnectionConstants_Qualities1";

	public static final String IConnnectionConstants_Qualities2 = "IConnnectionConstants_Qualities2";

	public static final String IConnnectionConstants_Qualities3 = "IConnnectionConstants_Qualities3";

	public static final String IConnnectionConstants_Qualities4 = "IConnnectionConstants_Qualities4";

	public static final String IConnnectionConstants_Qualities5 = "IConnnectionConstants_Qualities5";

	public static final String IConnnectionConstants_Qualities6 = "IConnnectionConstants_Qualities6";

	/*
	 * LanguageOptionsDialog
	 */
	public static final String LanguageOptionsDialog_Title = "LanguageOptionsDialog_Title";
	public static final String LanguageOptionsRestart = "LanguageOptionsRestart";

	/*
	 * MenuBarOptionsDialog
	 */
	public static final String MenuBarOptionsDialog_Tooltip_ImputText = "MenuBarOptionsDialog_Tooltip_ImputText";

	public static final String MenuBarOptionsDialog_Tooltip_GetScreenshot = "MenuBarOptionsDialog_Tooltip_GetScreenshot";

	public static final String MenuBarOptionsDialog_Tooltip_GetLog = "MenuBarOptionsDialog_Tooltip_GetLog";

	public static final String MenuBarOptionsDialog_Tooltip_PlayScript = "MenuBarOptionsDialog_Tooltip_PlayScript";

	/*
	 * MenuBarPanel
	 */
	public static final String MenuBarPanel_Tooltip_MainMenu = "MenuBarPanel_Tooltip_MainMenu";

	/*
	 * RemoteResources
	 */
	public static final String RemoteResources_Error_ConnectionError = "RemoteResources_Error_ConnectionError";

	/*
	 * RemoteResourcesConfiguration
	 */
	public static final String RemoteResourcesConfiguration_Error_UnableToLoad = "RemoteResourcesConfiguration_Error_UnableToLoad";
	public static final String RemoteResourcesConfiguration_Error_UnableToStore = "RemoteResourcesConfiguration_Error_UnableToStore";
	public static final String RemoteResourcesConfiguration_Error_StoreGenericError = "RemoteResourcesConfiguration_Error_StoreGenericError";
	public static final String RemoteResourcesConfiguration_Error_ConfigFileError = "RemoteResourcesConfiguration_Error_ConfigFileError";

	/*
	 * RemoteResourcesPanel
	 */
	public static final String RemoteResourcesPanel_UI_HeaderTitle = "RemoteResourcesPanel_UI_HeaderTitle";

	public static final String RemoteResourcesPanel_UI_ExitMessage = "RemoteResourcesPanel_UI_ExitMessage";

	public static final String RemoteResourcesPanel_UI_Tooltip_Switch2HostView = "RemoteResourcesPanel_UI_Tooltip_Switch2HostView";

	public static final String RemoteResourcesPanel_UI_Tooltip_Switch2DeviceView = "RemoteResourcesPanel_UI_Tooltip_Switch2DeviceView";

	public static final String RemoteResourcesPanel_UI_LocalTitle = "RemoteResourcesPanel_UI_LocalTitle";

	public static final String RemoteResourcesPanel_UI_RemoteTitle = "RemoteResourcesPanel_UI_RemoteTitle";

	public static final String RemoteResourcesPanel_UI_ExitMessageYes = "RemoteResourcesPanel_UI_ExitMessageYes";

	public static final String RemoteResourcesPanel_UI_ExitMessageNo = "RemoteResourcesPanel_UI_ExitMessageNo";

	/*
	 * RemoteResourcesServer
	 */
	public static final String RemoteResourcesServer_UI_ThreadName = "RemoteResourcesServer_UI_ThreadName";

	public static final String RemoteResourcesServer_Error_AdbNotFound = "RemoteResourcesServer_Error_AdbNotFound";

	public static final String RemoteResourcesServer_Error_InvalidPortNumber = "RemoteResourcesServer_Error_InvalidPortNumber";

	public static final String RemoteResourcesServer_Error_AdbNoPath = "RemoteResourcesServer_Error_AdbNoPath";

	public static final String RemoteResourcesServer_Error_NoPortNumber = "RemoteResourcesServer_Error_NoPortNumber";

	public static final String RemoteResourcesServer_Error_UnableToBind = "RemoteResourcesServer_Error_UnableToBind";

	public static final String RemoteResourcesServer_Error_WaitingForConnection = "RemoteResourcesServer_Error_WaitingForConnection";

	public static final String RemoteResourcesServer_Info_StartingServer = "RemoteResourcesServer_Info_StartingServer";

	public static final String RemoteResourcesServer_Info_StoppingServer = "RemoteResourcesServer_Info_StoppingServer";

	public static final String RemoteResourcesServer_Warning_NoConfigFile = "RemoteResourcesServer_Warning_NoConfigFile";

	/*
	 * ScriptConfigurationDialog
	 */
	public static final String ScriptConfigurationDialog_Title = "ScriptConfigurationDialog_Title";

	public static final String ScriptConfigurationDialog_FileLabel = "ScriptConfigurationDialog_FileLabel";

	public static final String ScriptConfigurationDialog_EnableEditionLabel = "ScriptConfigurationDialog_EnableEditionLabel";

	public static final String ScriptConfigurationDialog_PauseLabel = "ScriptConfigurationDialog_PauseLabel";

	public static final String ScriptConfigurationDialog_SleepLabel = "ScriptConfigurationDialog_SleepLabel";

	public static final String ScriptConfigurationDialog_SaveButton = "ScriptConfigurationDialog_SaveButton";

	public static final String ScriptConfigurationDialog_AddActionButton = "ScriptConfigurationDialog_AddActionButton";

	public static final String ScriptConfigurationDialog_InformationLabel = "ScriptConfigurationDialog_InformationLabel";

	public static final String ScriptConfigurationDialog_SaveFileMessage = "ScriptConfigurationDialog_SaveFileMessage";

	public static final String ScriptConfigurationDialog_ErrorTimeMessage = "ScriptConfigurationDialog_ErrorTimeMessage";

	public static final String ScriptConfigurationDialog_ErrorLabel = "ScriptConfigurationDialog_ErrorLabel";

	public static final String ScriptConfigurationDialog_ExecuteLabel = "ScriptConfigurationDialog_ExecuteLabel";

	public static final String ScriptConfigurationDialog_ExecuteActionButton = "ScriptConfigurationDialog_ExecuteActionButton";

	public static final String ScriptConfigurationDialog_ExecuteScriptButton = "ScriptConfigurationDialog_ExecuteScriptButton";

	public static final String ScriptConfigurationDialog_FinishedExecutionMessage = "ScriptConfigurationDialog_FinishedExecutionMessage";

	/*
	 * LogCaptureDialog
	 */
	public static final String LogCaptureDialog_Label = "LogCaptureDialog_Label";

	public static final String LogCaptureDialog_DirectoryLabel = "LogCaptureDialog_DirectoryLabel";

	public static final String LogCaptureDialog_LogLevel = "LogCaptureDialog_LogLevel";

	/*
	 * Generic Messages
	 */

	public static final String UI_Cancel = "UI_Cancel";

	public static final String UI_Finish = "UI_Finish";

	public static final String UI_Next = "UI_Next";

	public static final String UI_OK = "UI_OK";

	public static final String UI_Previous = "UI_Previous";

} public class Device {

	public static final String HEADER = "[device]";

	public static final String NAME = "name";

	public static final String SERIAL = "sn";

	public static final String HOST = "host";

	public static final String POLLING_RATE = "polling";

	public static final String IMAGE_QUALITY = "quality";

	public static final String SCRIPT_GENERATION = "script";

	public static final String SCRIPT_PATH = "scriptPath";

	public static final String SCRIPT_NAME = "scriptName";

	private final Map<String, String> deviceProperties = new HashMap<String, String>();

	public Device(String name, String host, String serialNumber) {
		deviceProperties.put(NAME, name);
		deviceProperties.put(HOST, host);
		deviceProperties.put(SERIAL, serialNumber);
		deviceProperties.put(SCRIPT_GENERATION, "false");
		deviceProperties
				.put(SCRIPT_PATH,
						RemoteResourcesConfiguration.getRemoteResourcesDir() != null ? RemoteResourcesConfiguration
								.getRemoteResourcesDir()
								+ File.separator
								+ "scripts" : System //$NON-NLS-1$
								.getProperty("user.home"));
		deviceProperties.put(SCRIPT_NAME, generateNameScriptFile(name, host));
		deviceProperties.put(POLLING_RATE,
				Long.toString(IConnectionConstants.DEFAULT_FRAME_DELAY));
		deviceProperties.put(IMAGE_QUALITY,
				Integer.toString(IConnectionConstants.DEFAULT_IMAGE_QUALITY));
	}

	private String generateNameScriptFile(String name, String host) {

		String date_mask = "yyyyMMdd";

		String device_nickname = "ScriptFile_";
		if (name != null) {
			device_nickname += name + "_";
		}

		java.util.Date today = new java.util.Date();
		Calendar cal = Calendar.getInstance();
		cal.setTime(today);
		SimpleDateFormat format_date = new SimpleDateFormat(date_mask);

		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(device_nickname);
		stringBuilder.append("_");
		stringBuilder.append(host);
		stringBuilder.append("_");
		stringBuilder.append(format_date.format(today));
		String script_name = stringBuilder.toString() + ".txt";

		return script_name;
	}

	public void setHost(String host) {
		deviceProperties.put(HOST, host);
	}

	public String getHost() {
		return deviceProperties.get(HOST);
	}

	public void setSerialNumber(String sn) {
		deviceProperties.put(SERIAL, sn);
	}

	public String getSerialNumber() {
		return deviceProperties.get(SERIAL);
	}

	public String getName() {
		return deviceProperties.get(NAME);
	}

	public void setName(String name) {
		deviceProperties.put(NAME, name);
	}

	public String getProperty(String property) {
		return deviceProperties.get(property);
	}

	public void setProperty(String property, String value) {
		deviceProperties.put(property, value);
	}

	public Map<String, String> getProperties() {
		return deviceProperties;
	}

	public boolean getScriptGeneration() {
		if (deviceProperties.get(SCRIPT_GENERATION).equals("true")) {
			return true;
		} else {
			return false;
		}
	}

	public void setScriptGeneration(boolean b) {
		if (b == true) {
			deviceProperties.put(SCRIPT_GENERATION, "true");
		} else {
			deviceProperties.put(SCRIPT_GENERATION, "false");
		}
	}

	public void write(FileOutputStream outputStream) throws IOException {
		StringBuilder builder = new StringBuilder();
		builder.append(HEADER);
		builder.append("\n");

		for (String key : deviceProperties.keySet()) {
			builder.append(key);
			builder.append("=");
			builder.append(deviceProperties.get(key));
			builder.append("\n");
		}

		outputStream.write(builder.toString().getBytes());
	}

	public static Device read(BufferedReader reader) throws IOException {

		boolean finished = false;
		String line;
		Map<String, String> properties = new HashMap<String, String>();

		reader.mark(1024);
		line = reader.readLine();
		if ((line != null) && line.startsWith("[")) {
			reader.reset();
			finished = true;
		}
		while ((line != null) && !finished) {
			properties.put(line.split("=")[0], line.split("=")[1]);
			reader.mark(1024);
			line = reader.readLine();
			if ((line != null) && line.startsWith("[")) {
				reader.reset();
				finished = true;
			}
		}
		Device dev = new Device(null, null, null);
		for (String prop : properties.keySet()) {
			dev.setProperty(prop, properties.get(prop));
		}
		return dev;
	}

	@Override
	public boolean equals(Object obj) {
		boolean equal = false;
		if (obj instanceof Device) {
			Device dev = (Device) obj;
			Map<String, String> otherDevProps = dev.getProperties();
			boolean areAllEqual = true;
			if (otherDevProps.size() == deviceProperties.size()) {
				for (String prop : deviceProperties.keySet()) {
					areAllEqual = areAllEqual
							&& deviceProperties.get(prop).equals(
									otherDevProps.get(prop));
				}
				equal = areAllEqual;
			}
		}
		return equal;
	}

	// copies the properties of this device into the argument
	public void copyProperties(Device d) {
		setHost(d.getHost());
		setName(d.getName());
		setProperty(POLLING_RATE, d.getProperty(POLLING_RATE));
		setProperty(IMAGE_QUALITY, d.getProperty(IMAGE_QUALITY));
		setProperty(SCRIPT_GENERATION, d.getProperty(SCRIPT_GENERATION));
		setProperty(SCRIPT_PATH, d.getProperty(SCRIPT_PATH));
		setProperty(SCRIPT_NAME, d.getProperty(SCRIPT_NAME));
	}
} public class Host {

	public static final String HEADER = "[host]";

	private static final String NAME = "name";

	private static final String HOSTNAME = "hostname";

	private static final String PORT = "port";

	private final List<Device> devices;

	private final Map<String, String> hostProperties = new HashMap<String, String>();

	public Host(String name, String hostname, int port) {
		hostProperties.put(NAME, name);
		hostProperties.put(HOSTNAME, hostname);
		hostProperties.put(PORT, Integer.toString(port));
		devices = new ArrayList<Device>();
	}

	public void addDevice(Device device) {
		devices.add(device);
	}

	public Host(String name) {
		this(name, null, 0);
	}

	public String getHostname() {
		return hostProperties.get(HOSTNAME);
	}

	public void setHostname(String hostname) {
		hostProperties.put(HOSTNAME, hostname);
	}

	public int getPort() {
		String portStr = hostProperties.get(PORT);
		int port = 0;
		try {
			port = Integer.parseInt(portStr);
		} catch (NumberFormatException e) {

		}
		return port;
	}

	public void setPort(int port) {
		hostProperties.put(PORT, Integer.toString(port));
	}

	public List<Device> getDevices() {
		return devices;
	}

	public String getName() {
		return hostProperties.get(NAME);
	}

	public void setName(String name) {
		hostProperties.put(NAME, name);
	}

	public void setProperty(String property, String value) {
		hostProperties.put(property, value);
	}

	public String getProperty(String property) {
		return hostProperties.get(property);
	}

	public Map<String, String> getProperties() {
		return hostProperties;
	}

	public void write(FileOutputStream outputStream) throws IOException {
		StringBuilder builder = new StringBuilder();
		builder.append(HEADER);
		builder.append("\n");

		for (String key : hostProperties.keySet()) {
			builder.append(key);
			builder.append("=");
			builder.append(hostProperties.get(key));
			builder.append("\n");
		}

		outputStream.write(builder.toString().getBytes());

	}

	public static Host read(BufferedReader reader) throws IOException {

		boolean finished = false;
		String line;
		Map<String, String> properties = new HashMap<String, String>();

		reader.mark(1024);
		line = reader.readLine();
		if ((line != null) && line.startsWith("[")) {
			reader.reset();
			finished = true;
		}
		while ((line != null) && !finished) {
			properties.put(line.split("=")[0], line.split("=")[1]);
			reader.mark(1024);
			line = reader.readLine();
			if ((line != null) && line.startsWith("[")) {
				reader.reset();
				finished = true;
			}
		}
		Host host = new Host(null, null, 0);
		for (String prop : properties.keySet()) {
			host.setProperty(prop, properties.get(prop));
		}
		return host;
	}

	@Override
	public boolean equals(Object obj) {
		boolean equal = false;
		if (obj instanceof Host) {
			Host host = (Host) obj;
			Map<String, String> otherHostProps = host.getProperties();
			boolean areAllEqual = true;
			if (otherHostProps.size() == hostProperties.size()) {
				for (String prop : hostProperties.keySet()) {
					areAllEqual = areAllEqual
							&& hostProperties.get(prop).equals(
									otherHostProps.get(prop));
				}
				equal = areAllEqual;
			}
		}
		return equal;
	}
} public class PersistentDeviceModel {

	public static final String LOCALHOST = "localhost";

	private final Map<String, Host> hosts;

	// contains only the devices currently connected
	private final Map<String, Device> devices;

	// contains devices that are connected, the ones that were disconnected
	// while RR was running and the ones in device.cfg
	private final Map<String, Device> devicesHistory;

	private final Set<DeviceModelChangeListener> listeners;

	public PersistentDeviceModel() {
		hosts = new HashMap<String, Host>();
		devices = new HashMap<String, Device>();
		devicesHistory = new HashMap<String, Device>();
		listeners = new HashSet<DeviceModelChangeListener>();
	}

	public Host getHost(String hostname) {
		return hosts.get(hostname);
	}

	public Collection<Host> getHosts() {
		return hosts.values();
	}

	public Collection<Device> getDevices() {
		return devices.values();
	}

	public Device getDevice(String name) {
		return devices.get(name);
	}

	public void addDevice(Device device) {
		addDevice(device, true);
	}

	/**
	 * Adds a new device to this model
	 * 
	 * @param device
	 *            the device to be added
	 * @param notifyListeners
	 *            notify or not the listeners
	 */
	private void addDevice(Device device, boolean notifyListeners) {
		if (devicesHistory.keySet().contains(device.getSerialNumber())) {
			devices.put(devicesHistory.get(device.getSerialNumber()).getName(),
					devicesHistory.get(device.getSerialNumber()));
		} else {
			devices.put(device.getName(), device);
			devicesHistory.put(device.getSerialNumber(), device);
		}

		Host host = hosts.get(device.getHost());
		if (!host.getDevices().contains(device)) {
			host.addDevice(device);
		}

		if (notifyListeners) {
			for (DeviceModelChangeListener listener : listeners) {
				listener.deviceAdded(device);
			}
		}
	}

	public void addHost(Host host) {
		if (hosts.get(host.getName()) == null) {
			hosts.put(host.getName(), host);
		}

		for (DeviceModelChangeListener listener : listeners) {
			listener.hostAdded(host);
		}

		for (Device device : host.getDevices()) {
			addDevice(device, false);
		}

	}

	public void removeDevice(Device device) {
		removeDevice(device, true);
	}

	private void removeDevice(Device device, boolean notify) {
		boolean disconnectDevice = true;
		Host h = hosts.get(device.getHost());

		if (h != null) {
			int option = JOptionPane
					.showOptionDialog(
							null,
							RemoteResourcesLocalization
									.getMessage(RemoteResourcesMessages.ConnectDeviceAction_RemoveDevice),
							RemoteResourcesLocalization
									.getMessage(RemoteResourcesMessages.ConnectDeviceAction_RemoveDeviceTitle),
							JOptionPane.YES_NO_OPTION,
							JOptionPane.QUESTION_MESSAGE,
							null,
							new String[] {
									RemoteResourcesLocalization
											.getMessage(RemoteResourcesMessages.ConnectDeviceAction_RemoveDeviceYes),
									RemoteResourcesLocalization
											.getMessage(RemoteResourcesMessages.ConnectDeviceAction_RemoveDeviceNo) },
							null);

			disconnectDevice = option == JOptionPane.YES_OPTION ? true : false;
		}

		if (disconnectDevice) {

			Device d = devices.remove(device.getName());
			if (!d.getHost().equalsIgnoreCase(LOCALHOST)) {
				devicesHistory.remove(d.getSerialNumber());
			}

			if (h != null) {
				h.getDevices().remove(d.getName());
			}
			if ((d != null) && notify) {
				for (DeviceModelChangeListener listener : listeners) {
					listener.deviceRemoved(d);
				}
			}
		} else {
			Device d = devices.get(device.getName());
			if ((d != null) && notify) {
				for (DeviceModelChangeListener listener : listeners) {
					if (listener instanceof DevicesContainer) {
						((DevicesContainer) listener).disableCapture(d);
					} else if (listener instanceof HostsContainer) {
						((HostsContainer) listener).disableCapture(d);
					}
				}
			}
		}
	}

	public void changeDeviceName(Device device, String oldName) {
		Device d = devices.get(oldName);
		if (d != null) {

			devices.remove(oldName);
			devices.put(device.getName(), device);

			for (DeviceModelChangeListener listener : listeners) {
				listener.deviceChangedName(device, oldName);
			}
		}
	}

	public void removeHost(String name) {
		Host h = hosts.remove(name);

		boolean disconnectHost = true;
		int option = JOptionPane
				.showConfirmDialog(
						null,
						RemoteResourcesLocalization
								.getMessage(RemoteResourcesMessages.HostPanel_RemoveHost),
						"Remove Host", JOptionPane.YES_NO_OPTION,
						JOptionPane.QUESTION_MESSAGE);

		disconnectHost = option == JOptionPane.YES_OPTION ? true : false;

		if (disconnectHost) {
			if (h != null) {
				for (Device device : h.getDevices()) {
					removeDevice(device, false);
					CaptureManager.getInstance().stopCapture();
				}
				for (DeviceModelChangeListener listener : listeners) {
					listener.hostRemoved(h);
				}
			}
		}
	}

	public void addModelChangedListener(DeviceModelChangeListener listener) {
		listeners.add(listener);
	}

	public void removeModelChangedListener(DeviceModelChangeListener listener) {
		listeners.remove(listeners);
	}

	public void save(File file) {
		FileOutputStream outputStream = null;
		try {
			if (!file.exists()) {
				file.getParentFile().mkdirs();
				file.createNewFile();
			}
			if (file.canWrite()) {
				outputStream = new FileOutputStream(file);
				for (Device device : devicesHistory.values()) {
					device.write(outputStream);
				}
				for (Host host : hosts.values()) {
					if (!LOCALHOST.equals(host.getHostname())) {
						host.write(outputStream);
					}
				}
			}
		} catch (IOException e) {

		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					// do nothing
				}
			}
		}
	}

	public void load(File file) {
		BufferedReader reader = null;
		try {
			if (file.canRead()) {
				reader = new BufferedReader(new FileReader(file));
				String line = reader.readLine();
				Map<String, Host> hosts = new HashMap<String, Host>();
				while (line != null) {
					if (line.trim().length() > 0) {
						if (Device.HEADER.equals(line)) {
							Device d = Device.read(reader);
							devicesHistory.put(d.getSerialNumber(), d);
							devices.put(d.getName(), d);
							Host h = hosts.get(d.getHost());
							if (h != null) {
								h.addDevice(d);
							} else {
								if (!LOCALHOST.equals(d.getHost())) {
									h = new Host(d.getHost());
									h.addDevice(d);
									hosts.put(h.getName(), h);
								}
							}

						} else if (Host.HEADER.equals(line)) {
							Host h = Host.read(reader);
							Host added = hosts.get(h.getName());
							if (added != null) {
								added.setHostname(h.getHostname());
								added.setPort(h.getPort());
							}
						}
					}
					line = reader.readLine();
				}
				for (Host h : hosts.values()) {
					addHost(h);
				}
			}
		} catch (IOException e) {

		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					// do nothing
				}
			}
		}
	}

} public enum WizardStatus {
	OK, INFO, WARNING, ERROR
}; public class DeviceUtils {

	private static DeviceUtils instance = null;
	private final Map<String, String> devices = new HashMap<String, String>();

	public static DeviceUtils getInstance() {
		if (instance == null) {
			instance = new DeviceUtils();
		}

		return instance;
	}

	public void setDeviceNickname(String device, String nickname) {
		devices.put(device, nickname);
	}

	public String getDeviceNickname(String device) {
		return devices.get(device);
	}

	public boolean changeNickname(String device, String newNickname,
			PersistentDeviceModel model) {

		boolean isChanged = false;

		for (Device dev : model.getDevices()) {
			if (dev.getName().equals(newNickname)
					&& !dev.getSerialNumber().equals(device)) {
				return false;
			}
		}
		if (devices.containsKey(device)) {
			devices.put(device, newNickname);
			isChanged = true;
		}

		return isChanged;
	}

}
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.border.BevelBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import com.eldorado.remoteresources.android.client.AndroidDeviceClient;
import com.eldorado.remoteresources.android.client.connection.Client;
import com.eldorado.remoteresources.android.client.connection.ClientConnectionManager;
import com.eldorado.remoteresources.i18n.RemoteResourcesLocalization;
import com.eldorado.remoteresources.i18n.RemoteResourcesMessages;
import com.eldorado.remoteresources.ui.model.Device;
import com.eldorado.remoteresources.ui.model.Host;
import com.eldorado.remoteresources.ui.model.PersistentDeviceModel;
import com.eldorado.remoteresources.ui.wizard.IWizard.WizardStatus;
import com.eldorado.remoteresources.utils.DeviceUtils;
						validatePage();
					}

					@Override
					public void changedUpdate(DocumentEvent e) {
						validatePage();
					}
				});
		constraints = new GridBagConstraints();
		constraints.gridy = 0;
		constraints.weightx = 1;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.insets = new Insets(2, 2, 2, 2);
		layout.addLayoutComponent(deviceNameText, constraints);
		add(deviceNameText);

		JScrollPane pane = new JScrollPane();
		pane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		constraints = new GridBagConstraints();
		constraints.weightx = 1;
		constraints.weighty = 1;
		constraints.gridy = 1;
		constraints.gridwidth = 2;
		constraints.fill = GridBagConstraints.BOTH;
		layout.addLayoutComponent(pane, constraints);
		add(pane);

		deviceList = new JTable();
		deviceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		deviceList.getSelectionModel().addListSelectionListener(
				new ListSelectionListener() {

					@Override
					public void valueChanged(ListSelectionEvent e) {
						validatePage();
					}
				});
		pane.setViewportView(deviceList);

	}

	public boolean validateDevice() {
		for (Device d : getModel().getDevices()) {
			if (d.getSerialNumber().equals(
					deviceList.getValueAt(deviceList.getSelectedRow(), 1))) {
				return true;
			}
		}
		return false;
	}

	@Override
	protected void validatePage() {

		String name = deviceNameText.getText();
		String errorMessage = null;
		WizardStatus status = WizardStatus.OK;

		if (name.trim().isEmpty()) {
			errorMessage = RemoteResourcesLocalization
					.getMessage(RemoteResourcesMessages.AddDevicePage_Error_NameIsEmpty);
			status = WizardStatus.ERROR;
		}

		if ((errorMessage == null) && (getModel().getDevice(name) != null)) {
			errorMessage = RemoteResourcesLocalization
					.getMessage(RemoteResourcesMessages.AddDevicePage_Error_DeviceAlreadyExists);
			status = WizardStatus.ERROR;
		}

		if ((errorMessage == null)
				&& (deviceList.getModel().getRowCount() == 0)) {
			errorMessage = RemoteResourcesLocalization
					.getMessage(RemoteResourcesMessages.AddDevicePage_Error_NoAvailableDevice);
			status = WizardStatus.ERROR;
		}
		if ((errorMessage == null)
				&& (deviceList.getSelectionModel().isSelectionEmpty())) {
			errorMessage = RemoteResourcesLocalization
					.getMessage(RemoteResourcesMessages.AddDevicePage_Error_NoSelectedDevice);
			status = WizardStatus.ERROR;
		}

		if ((errorMessage == null) && (validateDevice())) {
			errorMessage = RemoteResourcesLocalization
					.getMessage(RemoteResourcesMessages.AddDevicePage_Error_DeviceAlreadyCreated);
			status = WizardStatus.ERROR;
		}

		if (status != WizardStatus.ERROR) {
			DeviceUtils.getInstance().setDeviceNickname(
					device.getSerialNumber(), name);
			device.setName(name);
			device.setHost(aHost.getName());
			device.setSerialNumber(deviceList.getModel()
					.getValueAt(deviceList.getSelectedRow(), 1).toString());
		}

		setErrorMessage(errorMessage);
		setErrorLevel(status);
		super.validatePage();
	}

	@Override
	public void setVisible(boolean aFlag) {
		// Cells non editable
		DefaultTableModel model = new DefaultTableModel() {
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isCellEditable(int row, int column) {
				return false; // Every cell non-editable
			}

		};

		model.addColumn(RemoteResourcesLocalization
				.getMessage(RemoteResourcesMessages.AddDevicePage_UI_DeviceTableColumn_DeviceModel));
		model.addColumn(RemoteResourcesLocalization
				.getMessage(RemoteResourcesMessages.AddDevicePage_UI_DeviceTableColumn_DeviceSerialNumber));
		client = ClientConnectionManager.getInstance().getClient(