package alexander.martinz.libs.execution;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeoutException;

import alexander.martinz.libs.execution.binaries.Installer;
import alexander.martinz.libs.execution.exceptions.RootDeniedException;
import alexander.martinz.libs.logger.Logger;

public class ShellManager {
    private static ShellManager sInstance;

    private static ArrayList<RootShell> rootShells = new ArrayList<>();
    private static ArrayList<NormalShell> normalShells = new ArrayList<>();

    private ShellManager(@NonNull Context context) {
        cleanupShells();

        Installer.installBusyBox(context);
    }

    @NonNull public static ShellManager get(@NonNull Context context) {
        if (sInstance == null) {
            sInstance = new ShellManager(context);
        }
        return sInstance;
    }

    @Nullable public RootShell getRootShell() {
        return getRootShell(false);
    }

    @Nullable public RootShell getRootShell(boolean newShell) {
        RootShell rootShell;
        if (!newShell && rootShells.size() > 0) {
            rootShell = rootShells.get(0);
            if (rootShell != null) {
                return rootShell;
            }
        }

        rootShell = createRootShell();
        rootShells.add(rootShell);
        return rootShell;
    }

    @Nullable private RootShell createRootShell() {
        try {
            return new RootShell();
        } catch (IOException | TimeoutException | RootDeniedException e) {
            Logger.e(this, "Error creating new root shell", e);
        }
        return null;
    }

    @Nullable public NormalShell getNormalShell() {
        return getNormalShell(false);
    }

    @Nullable public NormalShell getNormalShell(boolean newShell) {
        NormalShell normalShell;
        if (!newShell && normalShells.size() > 0) {
            normalShell = normalShells.get(0);
            if (normalShell != null) {
                return normalShell;
            }
        }

        normalShell = createNormalShell();
        normalShells.add(normalShell);
        return normalShell;
    }

    @Nullable private NormalShell createNormalShell() {
        try {
            return new NormalShell();
        } catch (IOException | TimeoutException | RootDeniedException e) {
            Logger.e(this, "Error creating new shell", e);
        }
        return null;
    }

    public synchronized void cleanupRootShells() {
        if (rootShells == null) {
            rootShells = new ArrayList<>();
        }
        if (rootShells.size() > 0) {
            final Iterator<RootShell> rootShellIterator = rootShells.iterator();
            while (rootShellIterator.hasNext()) {
                final RootShell rootShell = rootShellIterator.next();
                rootShell.close();
                rootShellIterator.remove();
            }
            rootShells.clear();
        }
    }

    public synchronized void cleanupNormalShells() {
        if (normalShells == null) {
            normalShells = new ArrayList<>();
        }
        if (normalShells.size() > 0) {
            final Iterator<NormalShell> normalShellIterator = normalShells.iterator();
            while (normalShellIterator.hasNext()) {
                final NormalShell normalShell = normalShellIterator.next();
                normalShell.close();
                normalShellIterator.remove();
            }
            normalShells.clear();
        }
    }

    public synchronized int getNormalShellCount() {
        return normalShells.size();
    }

    public synchronized int getRootShellCount() {
        return rootShells.size();
    }

    public synchronized void cleanupShells() {
        cleanupRootShells();
        cleanupNormalShells();
    }

    public synchronized void onDestroy() {
        cleanupShells();
    }
}

@@ -714,265 +714,1437 @@
 ger(
-@NonNull Context context) %7B%0A        cleanupShells();%0A%0A        Installer.installBusyBox(context);%0A    %7D%0A%0A    @NonNull public static ShellManager get(@NonNull Context context) %7B%0A        if (sInstance == null) %7B%0A            sInstance = new ShellManager(context
+) %7B%0A        cleanupShells();%0A    %7D%0A%0A    @Deprecated @NonNull public static ShellManager get(@NonNull Context context) %7B%0A        return get();%0A    %7D%0A%0A    @NonNull public static ShellManager get() %7B%0A        if (sInstance == null) %7B%0A            sInstance = new ShellManager();%0A        %7D%0A        return sInstance;%0A    %7D%0A%0A    public ShellManager installBusyBox(@NonNull Context context) %7B%0A        Installer.installBusyBox(context);%0A        return this;%0A    %7D%0A%0A    @Nullable public Command runRootCommand(@NonNull String cmd) %7B%0A        return runRootCommand(cmd, false);%0A    %7D%0A%0A    @Nullable public Command runRootCommand(@NonNull String cmd, boolean waitForIt) %7B%0A        final RootShell rootShell = getRootShell();%0A        if (rootShell != null) %7B%0A            final Command command = new Command(cmd);%0A            rootShell.add(command);%0A            if (waitForIt) %7B%0A                command.waitFor();%0A            %7D%0A            return command;%0A        %7D%0A        return null;%0A    %7D%0A%0A    @Nullable public Command runShellCommand(@NonNull String cmd) %7B%0A        return runShellCommand(cmd, false);%0A    %7D%0A%0A    @Nullable public Command runShellCommand(@NonNull String cmd, boolean waitForIt) %7B%0A        final NormalShell normalShell = getNormalShell();%0A        if (normalShell != null) %7B%0A            final Command command = new Command(cmd);%0A            normalShell.add(command);%0A            if (waitForIt) %7B%0A                command.waitFor(
 );%0A 
@@ -2142,34 +2142,42 @@
 tFor();%0A        
-%7D%0A
+    %7D%0A    
         return s
@@ -2171,33 +2171,62 @@
      return 
-sInstance
+command;%0A        %7D%0A        return null
 ;%0A    %7D%0A%0A   

add a "shot and run" way to execute commands