public class ClientLogger {
	private static final Logger logger = Logger
			.getLogger("RemoteResourcesClient"); //$NON-NLS-1$

	private static final String DEFAULT_CLIENT_LOGGER_DIR = RemoteResourcesConfiguration
			.getRemoteResourcesDir() != null ? RemoteResourcesConfiguration
			.getRemoteResourcesDir() + File.separator + "logs" : System //$NON-NLS-1$
			.getProperty("user.home");

	private static final String CLIENT_LOGGER_FILENAME = "remote_resources_client.log"; //$NON-NLS-1$

	private ClientLogger() {
	};

	static {
		logger.setLevel(Level.FINE);
		String outputDir = RemoteResourcesConfiguration.getInstance()
				.get(RemoteResourcesConfiguration.LOG_DIR,
						DEFAULT_CLIENT_LOGGER_DIR);
		File fOutputDir = new File(outputDir);

		if (!fOutputDir.exists()) {
			fOutputDir.mkdirs();
		}

		if (fOutputDir.exists()) {
			String outputFile = outputDir + File.separator
					+ CLIENT_LOGGER_FILENAME;

			try {
				Handler fileHandler = new FileHandler(outputFile);
				fileHandler.setFormatter(new SimpleFormatter());
				logger.addHandler(fileHandler);
			} catch (Exception e) {
				System.err
						.println(RemoteResourcesLocalization
								.getMessage(RemoteResourcesMessages.ClientLogger_Error_UnableToConfigure));
				e.printStackTrace(System.err);
			}
		} else {
			System.err
					.println(RemoteResourcesLocalization
							.getMessage(
									RemoteResourcesMessages.ClientLogger_Error_InsufficientPermissions,
									fOutputDir.getAbsolutePath()));
		}
	}

	public static void error(Class<?> clazz, String message, Throwable exception) {
		logger.logp(Level.SEVERE, clazz.getName(), null, message, exception);
	}

	public static void warning(Class<?> clazz, String message,
			Throwable exception) {
		logger.logp(Level.WARNING, clazz.getName(), null, message, exception);
	}

	public static void info(Class<?> clazz, String message, Throwable exception) {
		logger.logp(Level.INFO, clazz.getName(), null, message, exception);
	}

	public static void debug(Class<?> clazz, String message, Throwable exception) {
		logger.logp(Level.FINE, clazz.getName(), null, message, exception);
	}

} 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 interface DataReceivedListener {

	/**
	 * Inform about a new data frame received
	 * 
	 * @param serialNumber
	 *            the device serialNumber
	 * @param frameContent
	 *            the content
	 */
	public void newFrameReceived(String serialNumber, byte[] frameContent);

} public enum KeyCommandMessageType {
	KEY_DOWN, KEY_UP
} public class TypeCommandMessage extends DeviceCommandMessage {

	private static final long serialVersionUID = 4661705511854391742L;

	private final String text;

	/**
	 * Create a new touch command
	 * 
	 * @param sequenceNumber
	 *            the message sequence number
	 * @param device
	 *            the device serial number
	 * @param text
	 *            the text to type
	 */
	public TypeCommandMessage(int sequenceNumber, String device, String text) {
		super(sequenceNumber, device, DeviceCommandMessageType.TYPE_COMMAND);
		this.text = text;
	}

	/**
	 * get the text to type
	 * 
	 * @return the text to type
	 */
	public String getText() {
		return text;
	}
} 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 Script {

	private static boolean generationScriptActive = false;

	private final static String TAG_TOUCH = "[TOUCH]";

	private final static String TYPE_OF_TOUCH = "[TYPE_OF_TOUCH]";

	private final static String COORDINATE_X = "[COR_X]";

	private final static String COORDINATE_Y = "[COR_Y]";

	private final static String TAG_KEYCOMMAND = "[KEYCOMMAND]";

	private final static String TYPE_KEY_PRESSED = "[TYPE_KEY_PRESSED]";

	private final static String TYPE_KEY_ORIENTATION = "[ORIENTATION]";

	private final static String PARAM = "[PARAM]";

	public final static String TAG_SLEEP = "[SLEEP]";

	private final static String TIME_TO_SLEEP = "[TTS]";

	public final static String TAG_PAUSE = "[PAUSE]";

	private final static StringBuilder key_model_builder = new StringBuilder();

	private final static StringBuilder touch_model_builder = new StringBuilder();

	private final static StringBuilder sleep_model_builder = new StringBuilder();

	private static String touch_model = null;

	private static String sleep_model = null;

	private static String pause_model = TAG_PAUSE;

	private static String key_model = null;

	private final static Vector<String> actions = new Vector<String>();

	private static Vector<DeviceCommandMessage> messages = new Vector<DeviceCommandMessage>();

	static {

		key_model_builder.append(TAG_KEYCOMMAND);
		key_model_builder.append(":");
		key_model_builder.append(TYPE_KEY_PRESSED);
		key_model_builder.append("=");
		key_model_builder.append(TYPE_KEY_ORIENTATION);
		key_model_builder.append("&");
		key_model_builder.append(PARAM);
		key_model = new String(key_model_builder.toString());

		touch_model_builder.append(TAG_TOUCH);
		touch_model_builder.append(":");
		touch_model_builder.append(TYPE_OF_TOUCH);
		touch_model_builder.append("-");
		touch_model_builder.append(COORDINATE_X);
		touch_model_builder.append("*");
		touch_model_builder.append(COORDINATE_Y);
		touch_model = new String(touch_model_builder.toString());

		sleep_model_builder.append(TAG_SLEEP);
		sleep_model_builder.append(":");
		sleep_model_builder.append(TIME_TO_SLEEP);
		sleep_model = new String(sleep_model_builder);

	}

	public static void registerActionTouch(TouchCommandMessageType touch,
			String cX, String cY) {

		actions.add(touch_model.replace(TYPE_OF_TOUCH, touch.toString())
				.replace(COORDINATE_X, cX).replace(COORDINATE_Y, cY));
	}

	public static void registerActionKey(String name, String param,
			KeyCommandMessageType orin) {
		actions.add(key_model.replace(TYPE_KEY_PRESSED, name)
				.replace(PARAM, param)
				.replace(TYPE_KEY_ORIENTATION, orin.name().toString()));
	}

	public static void clean() {
		messages.clear();
		actions.clear();
	}

	public static Vector<String> getActions() {
		return actions;
	}

	public static boolean getScriptGeneration() {
		return generationScriptActive;
	}

	public static void setScriptGeneration(boolean b) {
		generationScriptActive = b;
	}

	public static void loadScript(Vector<String> actions) {

		int sequenceNumber = 0;
		String serialNumber = "";

		for (String action : actions) {
			String[] a = action.split(":");

			// TODO: improve me!!! please! and what do we do with the PARAM?

			if (TAG_PAUSE.equals(a[0])) {
				ScriptCommandMessage scriptPauseMessage = new ScriptCommandMessage(
						sequenceNumber, serialNumber, null,
						ScriptCommandMessageType.PAUSE);
				messages.add(scriptPauseMessage);
			} else if (TAG_KEYCOMMAND.equals(a[0])) {

				String type = a[1].substring(0, a[1].indexOf("="));
				String orientation = a[1].substring(a[1].indexOf("=") + 1,
						a[1].indexOf("&"));
				String param = a[1].substring(a[1].indexOf("&") + 1,
						a[1].length());

				Keys k;
				if ("BACK".equals(type)) {
					k = Keys.BACK;
				} else if ("CAMERA".toString().equals(type)) {
					k = Keys.CAMERA;
				} else if ("CLEAR".toString().equals(type)) {
					k = Keys.CLEAR;
				} else if ("DEL".toString().equals(type)) {
					k = Keys.DEL;
				} else if ("FOCUS".toString().equals(type)) {
					k = Keys.FOCUS;
				} else if ("HOME".toString().equals(type)) {
					k = Keys.HOME;
				} else if ("MENU".toString().equals(type)) {
					k = Keys.MENU;
				} else if ("POWER".toString().equals(type)) {
					k = Keys.POWER;
				} else if ("SEARCH".toString().equals(type)) {
					k = Keys.SEARCH;
				} else if ("VOLUME_DOWN".toString().equals(type)) {
					k = Keys.VOLUME_DOWN;
				} else {
					k = Keys.VOLUME_UP;
				}

				KeyCommandMessageType kt;
				if (KeyCommandMessageType.KEY_UP.toString().equals(orientation)) {
					kt = KeyCommandMessageType.KEY_UP;
				} else {
					kt = KeyCommandMessageType.KEY_DOWN;
				}

				KeyCommandMessage keyMessage = new KeyCommandMessage(
						sequenceNumber, serialNumber, k, kt);

				messages.add(keyMessage);
			} else if (TAG_TOUCH.equals(a[0])) {

				String type = a[1].substring(0, a[1].indexOf("-"));
				String cor_X = a[1].substring(a[1].indexOf("-") + 1,
						a[1].indexOf("*"));
				String cor_Y = a[1].substring(a[1].indexOf("*") + 1,
						a[1].length());

				TouchCommandMessageType tt;
				if (TouchCommandMessageType.TOUCH_DOWN.toString().equals(type)) {
					tt = TouchCommandMessageType.TOUCH_DOWN;
				} else if (TouchCommandMessageType.TOUCH_UP.toString().equals(
						type)) {
					tt = TouchCommandMessageType.TOUCH_UP;
				} else {
					tt = TouchCommandMessageType.TOUCH_MOVE;
				}

				TouchCommandMessage touchMessage = new TouchCommandMessage(
						sequenceNumber, serialNumber, tt,
						Float.parseFloat(cor_X), Float.parseFloat(cor_Y));

				messages.add(touchMessage);

			} else if (TAG_SLEEP.equals(a[0])) {
				ScriptCommandMessage scriptSleepMessage = new ScriptCommandMessage(
						sequenceNumber, serialNumber, a[1],
						ScriptCommandMessageType.SLEEP);
				messages.add(scriptSleepMessage);
			} else {
				serialNumber = a[0];
			}

			sequenceNumber++;
		}

	}

	public static Vector<DeviceCommandMessage> getMessages() {
		return messages;
	}
} public abstract class Command {
	protected CommandType type;
	protected SubType subType;
	protected String[] params;

	public CommandType getCommandType() {
		return type;
	}

	public SubType getSubType() {
		return subType;
	}

	public abstract boolean areParametersValid();

	public String[] getParams() {
		return params;
	}
} public class CommandFactory {
	private static final String ADB_SHELL_COMMAND_PATTERN = "adb shell";
	private static final String ADB_PULL_COMMAND_PATTERN = "adb pull";
	private static final String ADB_PUSH_COMMAND_PATTERN = "adb push";
	private static final String ADB_INSTALL_COMMAND_PATTERN = "adb install";
	private static final String ADB_UNINSTALL_COMMAND_PATTERN = "adb uninstall";
	private static final String ADB_REBOOT_COMMAND_PATTERN = "adb reboot";

	public static Command getCommand(String command) {
		if (command == null) {
			return null;
		}

		if (command.startsWith(ADB_SHELL_COMMAND_PATTERN)) {
			Command c = new ShellCommand(CommandType.SHELL_COMMAND,
					SubType.NONE,
					new String[] { command.substring(ADB_SHELL_COMMAND_PATTERN
							.length()) });

			return c.areParametersValid() ? c : null;
		}

		if (command.startsWith(ADB_PULL_COMMAND_PATTERN)) {
			Command c = new FileHandlingCommand(CommandType.FILE_HANDLING,
					SubType.PULL, getParams(ADB_PULL_COMMAND_PATTERN, command));

			return c.areParametersValid() ? c : null;
		}

		if (command.startsWith(ADB_PUSH_COMMAND_PATTERN)) {
			Command c = new FileHandlingCommand(CommandType.FILE_HANDLING,
					SubType.PUSH, getParams(ADB_PUSH_COMMAND_PATTERN, command));

			return c.areParametersValid() ? c : null;
		}

		if (command.startsWith(ADB_INSTALL_COMMAND_PATTERN)) {
			Command c = new PackageHandlingCommand(
					CommandType.PACKAGE_HANDLING, SubType.INSTALL, getParams(
							ADB_INSTALL_COMMAND_PATTERN, command));

			return c.areParametersValid() ? c : null;
		}

		if (command.startsWith(ADB_UNINSTALL_COMMAND_PATTERN)) {
			Command c = new PackageHandlingCommand(
					CommandType.PACKAGE_HANDLING, SubType.UNINSTALL, getParams(
							ADB_UNINSTALL_COMMAND_PATTERN, command));

			return c.areParametersValid() ? c : null;
		}

		if (command.startsWith(ADB_REBOOT_COMMAND_PATTERN)) {
			Command c = new RebootCommand(CommandType.REBOOT, getParams(
					ADB_REBOOT_COMMAND_PATTERN, command));

			return c.areParametersValid() ? c : null;
		}

		return null;
	}

	private static String[] getParams(String commandPattern, String command) {
		String params = command.substring(commandPattern.length());

		return (null == params) ? null : params.trim().split(" ");
	}
} public class ImageUtils {

	/**
	 * 
	 * @param image
	 * @param scale
	 * @return
	 */
	public static BufferedImage resizeImage(BufferedImage image, double scale) {
		if ((scale == 1.0) || (image == null)) {
			return image;
		}

		int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image
				.getType();

		float width = image.getWidth();
		float height = image.getHeight();

		int newHeight = (int) Math.round(height * scale);
		int newWidth = Math.round((width * newHeight) / height);

		BufferedImage resizedImage = new BufferedImage(newWidth, newHeight,
				type);
		Graphics2D g = resizedImage.createGraphics();

		g.setComposite(AlphaComposite.Src);

		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		g.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		g.drawImage(image, 0, 0, newWidth, newHeight, null);
		g.dispose();

		return resizedImage;
	}

	public static float calculateResizeFactor(BufferedImage image,
			Dimension sizeToFit, int border) {
		if (image == null) {
			return 1;
		}

		double height = sizeToFit.getHeight() - (2 * border);
		double scaleHeight = (float) (height / image.getHeight());

		double width = sizeToFit.getWidth() - (2 * border);
		double scaleWidth = (float) (width / image.getWidth());

		float scale = (float) (scaleHeight <= scaleWidth ? scaleHeight
				: scaleWidth);

		return scale;
	}

	public static float calculateResizeFactor(BufferedImage image,
			Dimension sizeToFit) {
		return calculateResizeFactor(image, sizeToFit, 10);
	}

	/**
	 * <b>Compression temporarily disabled due to problems on full hd
	 * devices.</b> Compress a BufferedImage from chimpchat into a compressed Xz
	 * file with a Jpeg image within
	 * 
	 * @param sourceImage
	 *            the source image
	 * @param quality
	 *            the quality rate: 100 for max QUALITY, 0 for max COMPRESSION
	 * @return a byte array representing the image
	 * @throws IOException
	 *             if some error occurs during the conversion
	 */

	public static ByteArrayOutputStream compress(BufferedImage sourceImage,
			int quality) throws IOException {

		ByteArrayOutputStream imageOutput = new ByteArrayOutputStream();
		ByteArrayOutputStream xzOutput = new ByteArrayOutputStream();
		ImageWriter imgWriter = ImageIO.getImageWritersByFormatName("jpeg")
				.next();
		ImageWriteParam parameters = imgWriter.getDefaultWriteParam();
		parameters.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
		parameters.setCompressionQuality((quality > 100 ? 100
				: (quality < 0 ? 0 : quality)) / 100f);
		ImageOutputStream imgOutputStream = new MemoryCacheImageOutputStream(
				imageOutput);
		BufferedImage newImage = new BufferedImage(sourceImage.getWidth(),
				sourceImage.getHeight(), BufferedImage.TYPE_INT_RGB);
		ColorConvertOp colorConvertion = new ColorConvertOp(null);
		colorConvertion.filter(sourceImage, newImage);
		IIOImage image = new IIOImage(newImage, null, null);
		imgWriter.setOutput(imgOutputStream);
		imgWriter.write(null, image, parameters);
		/*
		 * XZOutputStream xzStream = new XZOutputStream(xzOutput, new
		 * LZMA2Options(LZMA2Options.PRESET_MAX));
		 */
		xzOutput.write(imageOutput.toByteArray());
		xzOutput.close();
		return xzOutput;
	}

	/**
	 * <b>Compression temporarily disabled due to problems on full hd
	 * devices.</b> Decompress a previously compressed Xz stream
	 * 
	 * @param xzStream
	 *            the compressed file
	 * @return the original file (notice that if file was compressed using JPEG,
	 *         you will not be able to retrieve the original file)
	 * @throws IOException
	 *             if any error occurs during decompression
	 */

	public static ByteArrayInputStream decompress(ByteArrayInputStream xzStream)
			throws IOException {
		ByteArrayInputStream xzInputStream = xzStream;
		byte firstByte = (byte) xzInputStream.read();
		byte[] buffer = new byte[xzInputStream.available()];
		buffer[0] = firstByte;
		xzInputStream.read(buffer, 1, buffer.length - 2);
		xzInputStream.close();
		return new ByteArrayInputStream(buffer);
	}

	public static boolean isEqual(BufferedImage newImage,
			BufferedImage previousImage) {
		boolean equal = true;

		if ((newImage != null) && (previousImage != null)) {
			DataBuffer newDb = newImage.getData().getDataBuffer();
			DataBuffer prevDb = previousImage.getData().getDataBuffer();

			for (int i = 0; (i < newDb.getSize()) && equal; i++) {
				if (newDb.getElem(i) != prevDb.getElem(i)) {
					return false;
				}
			}

		} else {
			equal = false;
		}

		return equal;
	}
} public class ResourcesLoader {
	public static final String RESOURCES_PATH = "com/eldorado/remoteresources/resources/";

	private static final Map<String, Image> cachedImages = new HashMap<String, Image>();

	private static final Map<String, Icon> cachedIcons = new HashMap<String, Icon>();

	/**
	 * 
	 * @param resourceName
	 * @return
	 */
	public static Image getImageResource(String resourceName, String extension,
			int size) {
		Image image = cachedImages.get(getImageKey(resourceName, size));

		if (image == null) {
			try {
				InputStream imageInputStream = loadResourceInputStream("images/"
						+ resourceName + extension);
				image = ImageIO.read(imageInputStream);
				if (size > 0) {
					if (image.getWidth(null) > image.getHeight(null)) {
						image = image.getScaledInstance(size, -1,
								Image.SCALE_SMOOTH);
					} else {
						image = image.getScaledInstance(-1, size,
								Image.SCALE_SMOOTH);
					}
				}
				cachedImages.put(getImageKey(resourceName, size), image);
			} catch (IOException e) {
				// TODO: log the information
			}
		}
		return image;
	}

	public static Image getImageResource(String resourceName, int size) {
		return getImageResource(resourceName, ".png", size);

	}

	public static Icon getIcon(String resourceName, int size) {
		return getIcon(resourceName, ".png", size);
	}

	public static Icon getIcon(String resourceName, String extension, int size) {
		Icon icon = cachedIcons.get(getImageKey(resourceName, size));
		if (icon == null) {
			icon = new ImageIcon(
					getImageResource(resourceName, extension, size));
			cachedIcons.put(getImageKey(resourceName, size), icon);
		}
		return icon;
	}

	public static Image getAnimatedGif(String resourceName) {
		URL url = ClassLoader.getSystemClassLoader().getResource(
				RESOURCES_PATH + "images/" + resourceName + ".gif");
		ImageIcon icon = new ImageIcon(url);
		return icon.getImage();
	}

	private static String getImageKey(String resourceName, int size) {
		return resourceName + "_" + size;
	}

	/**
	 * 
	 * @param resourceName
	 * @return
	 */
	public static Font getFontResource(String resourceName) {
		Font font;

		try {
			InputStream fontInputStream = loadResourceInputStream("fonts/"
					+ resourceName + ".ttf");
			font = Font.createFont(Font.PLAIN, fontInputStream);
		} catch (FontFormatException | IOException | NullPointerException e) {
			font = null;
		}

		return font;

	}

	/**
	 * 
	 * @param resourceName
	 * @return
	 */
	public static StringBuilder getFileResourceContent(String resourceName) {
		StringBuilder sb = new StringBuilder();
		BufferedReader br = new BufferedReader(new InputStreamReader(
				loadResourceInputStream("files/" + resourceName)));
		try {
			String line;
			for (; (line = br.readLine()) != null;) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			return null;
		} finally {
			try {
				br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return sb;

	}

	private static InputStream loadResourceInputStream(String resourceName) {
		ClassLoader classLoader = ClassLoader.getSystemClassLoader();

		InputStream resourceStream = classLoader
				.getResourceAsStream(RESOURCES_PATH + resourceName);

		return resourceStream;
	}
}
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ProgressMonitor;
import javax.swing.filechooser.FileNameExtensionFilter;
import com.eldorado.remoteresources.android.client.ClientLogger;
import com.eldorado.remoteresources.android.client.connection.Client;
import com.eldorado.remoteresources.android.client.connection.DataReceivedListener;
import com.eldorado.remoteresources.android.common.connection.messages.control.KeyCommandMessageType;
import com.eldorado.remoteresources.android.common.connection.messages.control.TypeCommandMessage;
import com.eldorado.remoteresources.i18n.RemoteResourcesLocalization;
import com.eldorado.remoteresources.i18n.RemoteResourcesMessages;
import com.eldorado.remoteresources.ui.model.Script;
import com.eldorado.remoteresources.utils.Command;
import com.eldorado.remoteresources.utils.CommandFactory;
import com.eldorado.remoteresources.utils.ImageUtils;
import com.eldorado.remoteresources.utils.ResourcesLoader;
/*
 * (C) Copyright 2014 Instituto de Pesquisas Eldorado (http://www.eldorado.org.br/).
 *
 * This file is part of the software Remote Resources
 *
 * All rights reserved. This file and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 3.0 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

package com.eldorado.remoteresources.ui;




public class MenuBarOptionsDialog extends JDialog {

	private static final long serialVersionUID = -5406368546124383299L;

	private Client client = null;

	private String serialNumber = null;

	private JButton keyBoardButton = null;

	private JButton cameraButton = null;

	private JButton logButton = null;

	// private JButton runnerButton = null;

	private BasePanel content = null;

	private boolean isDisplayed = false;

	// private boolean isWellLocated = false;

	private final ActionListener inputTextListener = new ActionListener() {

		@Override
		public void actionPerformed(ActionEvent e) {

			dispose();

			if ((client != null) && (serialNumber != null)) {
				InputTextDialog inputText = new InputTextDialog();
				inputText.setLocationRelativeTo(getParent());
				if (inputText.open()) {
					client.sendMessage(new TypeCommandMessage(client
							.getNextSequenceNumber(), serialNumber, inputText
							.getText()));

					Script.registerActionKey("KEYBOARD", inputText.getText(),
							KeyCommandMessageType.KEY_DOWN);
					Script.registerActionKey("KEYBOARD", inputText.getText(),
							KeyCommandMessageType.KEY_UP);
				}
			}
		}
	};

	private File lastUsedDirectory = null;

	private final ActionListener getScreenshotListener = new ActionListener() {

		@Override
		public void actionPerformed(ActionEvent e) {

			dispose();

			if ((client != null) && (serialNumber != null)) {
				FileNameExtensionFilter filter = new FileNameExtensionFilter(
						"JPG", "jpg");
				JFileChooser chooser = new JFileChooser(lastUsedDirectory);
				chooser.setFileFilter(filter);
				int returnVal = chooser.showSaveDialog(null);
				lastUsedDirectory = chooser.getSelectedFile() != null ? chooser
						.getSelectedFile().getParentFile() : null;
				if (returnVal == JFileChooser.APPROVE_OPTION) {
					final File saveFile = chooser.getSelectedFile();
					final File saveFile2 = new File(saveFile.getAbsolutePath()
							+ ".jpg");
					final ProgressMonitor monitor = new ProgressMonitor(null,
							"Waiting for screenshot", "Waiting", 0, 100);
					monitor.setMillisToDecideToPopup(1);
					monitor.setMillisToPopup(100);
					monitor.setProgress(10);

					client.takeScreenshot(serialNumber,
							new DataReceivedListener() {

								@Override
								public void newFrameReceived(
										String serialNumber, byte[] frameContent) {
									try {
										monitor.setProgress(50);
										ImageIO.write(
												ImageIO.read(ImageUtils