// Path: src/main/java/com/rmn/qa/AutomationConstants.java
// public interface AutomationConstants {
// 
//     // These NODE_CONFIG_* constants represent key names in the node configs
//     String CONFIG_CREATED_DATE = "createdDate";
//     String CONFIG_MAX_SESSION = "maxSession";
//     String CONFIG_BROWSER = "createdBrowser";
//     String CONFIG_OS = "createdOs";
//     // This is the value that will be in the desired capabilities that our node registers with
//     // Runtime value of the hub instance id that gets passed in as a system property.  Also used in the node config
//     String INSTANCE_ID = "instanceId";
//     // IP address of the hub that will be passed in as a system property
//     String IP_ADDRESS = "ipAddress";
//     // Test run UUID
//     String UUID = "uuid";
//     String WINDOWS_PROPERTY_NAME="node.windows.json";
//     String LINUX_PROPERTY_NAME="node.linux.json";
//     String EXTRA_CAPABILITIES_PROPERTY_NAME="extraCapabilities";
//     String AWS_ACCESS_KEY="awsAccessKey";
//     String AWS_PRIVATE_KEY="awsSecretKey";
//     String AWS_DEFAULT_RESOURCE_NAME= "aws.properties.default";
//     String REAPER_THREAD_CONFIG = "useReaperThread";
// }
// 
// Path: src/main/java/com/rmn/qa/AutomationUtils.java
// public final class AutomationUtils {
// 
//     /**
//      * Modifies the specified date
//      * @param dateToModify Date to modify
//      * @param unitsToModify Number of units to modify (e.g. 6 for 6 seconds)
//      * @param unitType Measurement type (e.g. Calendar.SECONDS)
//      * @return Modified date
//      */
//     public static Date modifyDate(Date dateToModify,int unitsToModify,int unitType) {
//         Calendar c = Calendar.getInstance();
//         c.setTime(dateToModify);
//         // Add 60 seconds so we're as close to the hour as we can be instead of adding 55 again
//         c.add(unitType,unitsToModify);
//         return c.getTime();
//     }
// 
//     /**
//      * Returns true if the current time is after the specified date, false otherwise
//      * @param dateToCheck Date to check against the current time
//      * @param unitsToCheckWith Number of units to add/subtract from dateToCheck
//      * @param unitType Unit type (e.g. Calendar.MINUTES)
//      * @return
//      */
//     public static boolean isCurrentTimeAfterDate(Date dateToCheck,int unitsToCheckWith,int unitType) {
//         Date targetDate = AutomationUtils.modifyDate(dateToCheck,unitsToCheckWith,unitType);
//         return new Date().after(targetDate);
//     }
// 
//     /**
//      * Returns true if the strings are lower case equal
//      * @param string1 First string to compare
//      * @param string2 Second string to compare
//      * @return
//      */
//     public static boolean lowerCaseMatch(String string1, String string2) {
//         string2 = string2.toLowerCase().replace(" ","");
//         return string2.equals(string1.toLowerCase().replace(" ", ""));
//     }
// 
// }
// 
// Path: src/main/java/com/rmn/qa/NodesCouldNotBeStartedException.java
// public class NodesCouldNotBeStartedException extends Exception {
//     public NodesCouldNotBeStartedException(String msg) {
//         super(msg);
//     }
// 
//     public NodesCouldNotBeStartedException(String msg, Throwable t) {
//         super(msg,t);
//     }
// }


import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.codec.binary.Base64;
import org.openqa.selenium.remote.BrowserType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.amazonaws.services.ec2.model.InstanceStateChange;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.TerminateInstancesResult;
import com.google.common.annotations.VisibleForTesting;
import com.rmn.qa.AutomationConstants;
import com.rmn.qa.AutomationUtils;
import com.rmn.qa.NodesCouldNotBeStartedException;
        if (keyName != null) {
            log.info("Setting keyname:" + keyName);
            runRequest.withKeyName(keyName);
        }

        log.info("Sending run request to AWS...");

        RunInstancesResult runInstancesResult = getResults(runRequest, 0);
        log.info("Run request result returned.  Adding tags");

        // Tag the instances with the standard RMN AWS data
        List<Instance> instances = runInstancesResult.getReservation().getInstances();
        if (instances.size() == 0) {
            throw new NodesCouldNotBeStartedException(String.format(
                    "Error starting up nodes -- count was zero and did not match expected count of %d", numberToStart));
        }

        associateTags(new Date().toString(), instances);
        return instances;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Instance> launchNodes(final String uuid, String os, final String browser, final String hubHostName,
            final int nodeCount, final int maxSessions) throws NodesCouldNotBeStartedException {

        // Unspecified OS will default to Linux
        if (null == os) {
// Path: src/main/java/com/rmn/qa/AutomationConstants.java
// public interface AutomationConstants {
// 
//     // These NODE_CONFIG_* constants represent key names in the node configs
//     String CONFIG_CREATED_DATE = "createdDate";
//     String CONFIG_MAX_SESSION = "maxSession";
//     String CONFIG_BROWSER = "createdBrowser";
//     String CONFIG_OS = "createdOs";
//     // This is the value that will be in the desired capabilities that our node registers with
//     // Runtime value of the hub instance id that gets passed in as a system property.  Also used in the node config
//     String INSTANCE_ID = "instanceId";
//     // IP address of the hub that will be passed in as a system property
//     String IP_ADDRESS = "ipAddress";
//     // Test run UUID
//     String UUID = "uuid";
//     String WINDOWS_PROPERTY_NAME="node.windows.json";
//     String LINUX_PROPERTY_NAME="node.linux.json";
//     String EXTRA_CAPABILITIES_PROPERTY_NAME="extraCapabilities";
//     String AWS_ACCESS_KEY="awsAccessKey";
//     String AWS_PRIVATE_KEY="awsSecretKey";
//     String AWS_DEFAULT_RESOURCE_NAME= "aws.properties.default";
//     String REAPER_THREAD_CONFIG = "useReaperThread";
// }
// 
// Path: src/main/java/com/rmn/qa/AutomationUtils.java
// public final class AutomationUtils {
// 
//     /**
//      * Modifies the specified date
//      * @param dateToModify Date to modify
//      * @param unitsToModify Number of units to modify (e.g. 6 for 6 seconds)
//      * @param unitType Measurement type (e.g. Calendar.SECONDS)
//      * @return Modified date
//      */
//     public static Date modifyDate(Date dateToModify,int unitsToModify,int unitType) {
//         Calendar c = Calendar.getInstance();
//         c.setTime(dateToModify);
//         // Add 60 seconds so we're as close to the hour as we can be instead of adding 55 again
//         c.add(unitType,unitsToModify);
//         return c.getTime();
//     }
// 
//     /**
//      * Returns true if the current time is after the specified date, false otherwise
//      * @param dateToCheck Date to check against the current time
//      * @param unitsToCheckWith Number of units to add/subtract from dateToCheck
//      * @param unitType Unit type (e.g. Calendar.MINUTES)
//      * @return
//      */
//     public static boolean isCurrentTimeAfterDate(Date dateToCheck,int unitsToCheckWith,int unitType) {
//         Date targetDate = AutomationUtils.modifyDate(dateToCheck,unitsToCheckWith,unitType);
//         return new Date().after(targetDate);
//     }
// 
//     /**
//      * Returns true if the strings are lower case equal
//      * @param string1 First string to compare
//      * @param string2 Second string to compare
//      * @return
//      */
//     public static boolean lowerCaseMatch(String string1, String string2) {
//         string2 = string2.toLowerCase().replace(" ","");
//         return string2.equals(string1.toLowerCase().replace(" ", ""));
//     }
// 
// }
// 
// Path: src/main/java/com/rmn/qa/NodesCouldNotBeStartedException.java
// public class NodesCouldNotBeStartedException extends Exception {
//     public NodesCouldNotBeStartedException(String msg) {
//         super(msg);
//     }
// 
//     public NodesCouldNotBeStartedException(String msg, Throwable t) {
//         super(msg,t);
//     }
// }


// Path: src/main/java/com/rmn/qa/aws/AwsVmManager.java
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.codec.binary.Base64;
import org.openqa.selenium.remote.BrowserType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.amazonaws.services.ec2.model.InstanceStateChange;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.TerminateInstancesResult;
import com.google.common.annotations.VisibleForTesting;
import com.rmn.qa.AutomationConstants;
import com.rmn.qa.AutomationUtils;
import com.rmn.qa.NodesCouldNotBeStartedException;

        if (keyName != null) {
            log.info("Setting keyname:" + keyName);
            runRequest.withKeyName(keyName);
        }

        log.info("Sending run request to AWS...");

        RunInstancesResult runInstancesResult = getResults(runRequest, 0);
        log.info("Run request result returned.  Adding tags");

        // Tag the instances with the standard RMN AWS data
        List<Instance> instances = runInstancesResult.getReservation().getInstances();
        if (instances.size() == 0) {
            throw new NodesCouldNotBeStartedException(String.format(
                    "Error starting up nodes -- count was zero and did not match expected count of %d", numberToStart));
        }

        associateTags(new Date().toString(), instances);
        return instances;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Instance> launchNodes(final String uuid, String os, final String browser, final String hubHostName,
            final int nodeCount, final int maxSessions) throws NodesCouldNotBeStartedException {

        // Unspecified OS will default to Linux
        if (null == os) {
            if (AutomationUtils.lowerCaseMatch(browser, "internet explorer")) {