package org.jetbrains.plugins.ruby.motion.run;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.UnixProcessManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.util.PathUtil;
import com.intellij.util.Processor;
import com.jetbrains.cidr.execution.ExecutionResult;
import com.jetbrains.cidr.execution.ProcessHandlerWithPID;
import com.jetbrains.cidr.execution.deviceSupport.AMDevice;
import com.jetbrains.cidr.execution.deviceSupport.AMDeviceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.ruby.run.RubyProcessHandler;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;
/**
* @author Dennis.Ushakov
*/
public class MotionDeviceProcessHandler extends RubyProcessHandler implements DeviceProcessHandler, ProcessHandlerWithPID {
private static final Logger LOG = Logger.getInstance(MotionDeviceProcessHandler.class);
final ExecutionResult<String> myExecutableName = new ExecutionResult<>();
final ExecutionResult<String> myLocalPath = new ExecutionResult<>();
final ExecutionResult<String> myRemotePath = new ExecutionResult<>();
final ExecutionResult<AMDevice> myDevice = new ExecutionResult<>();
@NotNull private final GeneralCommandLine myCommandLine;
protected MotionDeviceProcessHandler(@NotNull Process process,
@NotNull GeneralCommandLine commandLine,
@Nullable String runnerId) {
super(process, commandLine.getCommandLineString(), RubyProcessHandler.getOutputEncoding(), runnerId);
myCommandLine = commandLine;
}
@Override
public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) {
if (text.trim().endsWith(".ipa") && !myLocalPath.isDone()) {
myLocalPath.set(text.replace(".ipa", ".app").trim());
myExecutableName.set(text.substring(text.lastIndexOf("/") + 1, text.lastIndexOf(".")).replace("_spec", ""));
super.coloredTextAvailable(text, attributes);
} else if (text.startsWith("remote_app_path: ")) {
myRemotePath.set(text.substring(text.indexOf(" ")).trim());
} else if (!text.startsWith("debug_server_socket_path: ") && !text.startsWith("device_support_path: ")) {
super.coloredTextAvailable(text, attributes);
}
}
private void findDevice() {
if (myDevice.isDone()) return;
if (AMDeviceManager.getInstance().getConnectedDevices().isEmpty()) {
LOG.warn("No connected devices");
myDevice.set(null);
return;
}
String deviceId = null;
while (deviceId == null) {
deviceId = getDeviceId();
}
final AMDevice device = AMDeviceManager.getInstance().getDevice(deviceId);
LOG.assertTrue(device != null, "Failed to find device by id: " + deviceId);
myDevice.set(device);
}
@NotNull
@Override
public String getRemotePath() throws ExecutionException {
return myRemotePath.get() + "/" + myExecutableName.get();
}
@NotNull
@Override
public String getLocalPath() throws ExecutionException {
return PathUtil.getCanonicalPath(getWorkingDirectory() + "/" + myLocalPath.get() + "/" + myExecutableName.get());
}
@NotNull
@Override
public String getWorkingDirectory() {
return myCommandLine.getWorkDirectory().getAbsolutePath();
}
@NotNull
@Override
public Map<String, String> getEnvironment() {
return myCommandLine.getEnvironment();
}
@NotNull
@Override
public AMDevice getDevice() throws ExecutionException {
findDevice();
return myDevice.get();
}
@Override
public int getPID() throws ExecutionException {
return -1;
}
@Override
public int getPID(long timeout) throws ExecutionException, TimeoutException {
return -1;
}
private String getDeviceId() {
final Ref<String> deviceId = new Ref<>();
final int rakePid = UnixProcessManager.getProcessPid(myProcess);
UnixProcessManager.processPSOutput(UnixProcessManager.getPSCmd(false, false), s -> {
final Scanner scanner = new Scanner(s);
final int ppid = scanner.nextInt();
final int pid = scanner.nextInt();
final String command = scanner.nextLine();
if (ppid == rakePid && command.contains("deploy")) {
final Scanner commandScanner = new Scanner(command);
final String deploy = commandScanner.next();
if (!commandScanner.hasNext()) return false;
final String id = commandScanner.next();
deviceId.set("-d".equals(id) ? commandScanner.next() : id);
return true;
}
return false;
});
return deviceId.get();
}
}