/**
* Licensed Materials - Property of IBM
* 5748-XX8
* (C) Copyright IBM Corp. 2013, 2014 All Rights Reserved
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with
* IBM Corp.
**/

package com.ibm.rational.air.plugin.android;

import com.ibm.rational.air.plugin.android.ADBCommandHelper;
import com.ibm.rational.air.plugin.android.Util;
import com.urbancode.air.CommandHelper;

/**
* A utility class for helping to manage emulators.
**/
public class EmulatorHelper {
    // Contains the path to ADB.
    def adb
    // Contains the path to the android tool.
    def android
    // Contains the path to the emulator tool.
    def emulator

    public EmulatorHelper(String pathToSDK, boolean isWindows) {
        // Find the Android SDK directory.
        def sdkDir;
        
        try {
            sdkDir = new File(pathToSDK);
        } catch (Exception e) {
            System.out.println("An error occurred during an attempt to access the Android SDK " +
                "directory: " + e.getMessage());
            System.exit(1);
        }

        if(!sdkDir.absolute) {
            def workDir = new File('.').getCanonicalFile();
            sdkDir = new File(workDir, pathToSDK);
        }
        
        if(!sdkDir.directory) {
            System.out.println("Error: The path to the android SDK " +
                    "is incorrect: " + sdkDir.canonicalPath);
            System.exit(1);
        }
        
        // Obtain the path to ADB.
        adb = new ADBCommandHelper(sdkDir.canonicalPath, isWindows);
        
        // Find the path to the android and emulator tools.
        def pathToTools;
        try {
            pathToTools = new File(sdkDir, File.separator + "tools");
        } catch (Exception e) {
            System.out.println("An error occurred during an attempt to access the Android SDK " +
                "tools directory: " + e.getMessage());
            System.exit(1);
        }
        
        def androidExt = (isWindows?"/android.bat":"/android");
        def emulatorExt = (isWindows?"/emulator.exe":"/emulator");
        
        try {
            android = new File(pathToTools, androidExt);
            if(!android.file) {
                System.out.println("Error: The path to the android executable " +
                    "is incorrect: " + android.canonicalPath);
                System.exit(1);
            }
            
            emulator = new File(pathToTools, emulatorExt);
            if(!emulator.file) {
                System.out.println("Error: The path to the emulator executable file " +
                    "is incorrect: " + emulator.canonicalPath);
                System.exit(1);
            }
        } catch (Exception e) {
            System.out.println("An error occurred during an attempt to access the Android SDK " +
                "tools: " + e.getMessage());
            System.exit(1);
        }
    }
    
    /**
    * Creates an emulator that is based on the provided arguments.
    * See http://developer.android.com/tools/help/android.html.
    * name: The name of the emulator.
    * id: The target ID to use for creating the emulator.
    * cpu: The CPU or application binary interface (ABI) of the emulator to create.
    * force: Specifies whether to force the creation of the emulator.
    * snapshot: Specifies whether to create a snapshot on emulator creation.
    * arguments: A space-separated list or property file of arguments.
    **/
    public void createEmulator(String name, String id, String cpu, boolean force, 
        boolean snapshot, def arguments) {
        def args = [android.canonicalPath, "create", "avd", "-n", name, "-t", id];
        if(cpu) {
            args << "-b" << cpu;
        }
        
        if(force) {
            args << "-f";
        }
        
        if(snapshot) {
            args << "-a";
        }
        
        if(arguments) {
            args = Util.handleArgs(arguments, args);
        }
        
        // Run the command with the input no on the create custom 
        // hardware profile question.
        def ch = new CommandHelper(new File('.'));
        ch.ignoreExitValue(true);
        def result = ch.runCommand(null, args, "no");
        if(result != 0) {
            println "Error: An error occurred creating the emulator.";
        }
        System.exit(result);
    }
    
    /**
    * Removes an emulator based on the name provided.
    * See: http://developer.android.com/tools/help/android.html
    * name The name of the emulator.
    **/
    public void removeEmulator(String name) {
        def args = [android.canonicalPath, "delete", "avd", "-n", name];
        def ch = new CommandHelper(new File('.'));
        ch.ignoreExitValue(true);
        def result = ch.runCommand(null, args) {
            proc ->
            def out = new PrintStream(System.out, true)
            def builder = new StringBuilder()
            try {
                // forward stdout to autoflushing output stream, store stderr
                proc.waitForProcessOutput(out, builder)
            }
            finally {
                out.flush();
            }
            // Look for any errors that occurred while removing the emulator.
            def output = builder.toString();
            if(output.contains("Error")) {
                println "Error: The emulator could not be removed.";
                println output;
                System.exit(-1);
            }
        }
        if(result != 0) {
            println "Error: An error occurred during emulator removal.";
        }
        System.exit(result);
    }
    
    public String getEmulatorPath() {
        return emulator.canonicalPath;
    }
    
    /**
    * Runs the Android tool with the provided optional arguments.
    * arguments: A space-separated list or property file of arguments.
    * timeout: A period after which the Android command is stopped in
    *    milliseconds.
    * See http://developer.android.com/tools/help/android.html.
    **/
    public void androidCmd(def arguments, def timeout) {
        def args = [];
        if(arguments) {
            args = Util.handleArgs(arguments, args);
        }
        System.exit(runAndroidCmd(null, args, timeout));
    }
    
    /**
    * Runs the Android Debug Bridge command with the provided options.
    * See http://developer.android.com/tools/help/adb.html.
    * message: An optional message to output when the command is run.
    * option: An optional option to run the command with, for example, version, shell.
    * arguments: The arguments to run the command with.
    * timeout: A period after which the Android command is stopped in
    *    milliseconds.
    * Returns the exit code of the command.
    **/
    private int runAndroidCmd(def message, def arguments, def timeout) {
        def args = [android.canonicalPath];
        if(!arguments) {
            System.out.println("Error: Arguments must be provided for the Android command.");
            System.exit(1);
        }
        
        args = args.plus(args.size(), arguments);
        
        def ch = new CommandHelper(new File('.'));
        ch.ignoreExitValue = true;
        def startTime = System.currentTimeMillis();
        def result = ch.runCommand(message, args) {
            proc ->
            def out = new PrintStream(System.out, true)
            def err = new PrintStream(System.err, true)
            try {
                if(timeout) {
                    // forward stdout and stderr to autoflushing output stream
                    proc.consumeProcessOutput(out, err)
                    println "A timeout of " + timeout + " milliseconds is enabled.";
                    proc.waitForOrKill(Long.parseLong(timeout));
                } else {
                    proc.waitForProcessOutput(out, err);
                }
            }
            finally {
                out.flush();
                err.flush();
            }
        }
        if(result != 0) {
            println "Error: Running the Android command failed with this error code: " + result;
            if(timeout) {
                println "The timeout period might have been exceeded."
            }
        }
        return result;
    }
}