/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.debugger.impl;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.intellij.debugger.DebugEnvironment;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.DefaultDebugEnvironment;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JavaDebugProcess;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.tree.render.BatchEvaluator;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.JavaPatchableProgramRunner;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
import consulo.java.debugger.impl.GenericDebugRunnerConfiguration;
import consulo.java.execution.configurations.OwnJavaParameters;

public class GenericDebuggerRunner extends JavaPatchableProgramRunner<GenericDebuggerRunnerSettings>
{
	@Override
	public boolean canRun(@Nonnull final String executorId, @Nonnull final RunProfile profile)
	{
		return executorId.equals(DefaultDebugExecutor.EXECUTOR_ID) && profile instanceof GenericDebugRunnerConfiguration;
	}

	@Override
	@Nonnull
	public String getRunnerId()
	{
		return DebuggingRunnerData.DEBUGGER_RUNNER_ID;
	}

	@Override
	protected RunContentDescriptor doExecute(@Nonnull RunProfileState state, @Nonnull ExecutionEnvironment env) throws ExecutionException
	{
		FileDocumentManager.getInstance().saveAllDocuments();
		return createContentDescriptor(state, env);
	}

	@Nullable
	protected RunContentDescriptor createContentDescriptor(@Nonnull RunProfileState state, @Nonnull ExecutionEnvironment environment) throws ExecutionException
	{
		if(state instanceof JavaCommandLine)
		{
			final OwnJavaParameters parameters = ((JavaCommandLine) state).getJavaParameters();
			runCustomPatchers(parameters, environment.getExecutor(), environment.getRunProfile());
			RemoteConnection connection = DebuggerManagerImpl.createDebugParameters(parameters, true, DebuggerSettings.getInstance().DEBUGGER_TRANSPORT, "", false);
			return attachVirtualMachine(state, environment, connection, true);
		}
		if(state instanceof PatchedRunnableState)
		{
			final RemoteConnection connection = doPatch(new OwnJavaParameters(), environment.getRunnerSettings());
			return attachVirtualMachine(state, environment, connection, true);
		}
		if(state instanceof RemoteState)
		{
			final RemoteConnection connection = createRemoteDebugConnection((RemoteState) state, environment.getRunnerSettings());
			return attachVirtualMachine(state, environment, connection, false);
		}

		return null;
	}

	@javax.annotation.Nullable
	protected RunContentDescriptor attachVirtualMachine(RunProfileState state, @Nonnull ExecutionEnvironment env, RemoteConnection connection, boolean pollConnection) throws ExecutionException
	{
		DebugEnvironment environment = new DefaultDebugEnvironment(env, state, connection, pollConnection);
		final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(env.getProject()).attachVirtualMachine(environment);
		if(debuggerSession == null)
		{
			return null;
		}

		final DebugProcessImpl debugProcess = debuggerSession.getProcess();
		if(debugProcess.isDetached() || debugProcess.isDetaching())
		{
			debuggerSession.dispose();
			return null;
		}
		if(environment.isRemote())
		{
			// optimization: that way BatchEvaluator will not try to lookup the class file in remote VM
			// which is an expensive operation when executed first time
			debugProcess.putUserData(BatchEvaluator.REMOTE_SESSION_KEY, Boolean.TRUE);
		}

		return XDebuggerManager.getInstance(env.getProject()).startSession(env, session ->
		{
			XDebugSessionImpl sessionImpl = (XDebugSessionImpl) session;
			ExecutionResult executionResult = debugProcess.getExecutionResult();
			sessionImpl.addExtraActions(executionResult.getActions());
			if(executionResult instanceof DefaultExecutionResult)
			{
				sessionImpl.addRestartActions(((DefaultExecutionResult) executionResult).getRestartActions());
				sessionImpl.addExtraStopActions(((DefaultExecutionResult) executionResult).getAdditionalStopActions());
			}
			return JavaDebugProcess.create(session, debuggerSession);
		}).getRunContentDescriptor();
	}

	private static RemoteConnection createRemoteDebugConnection(RemoteState connection, final RunnerSettings settings)
	{
		final RemoteConnection remoteConnection = connection.getRemoteConnection();

		GenericDebuggerRunnerSettings debuggerRunnerSettings = (GenericDebuggerRunnerSettings) settings;

		if(debuggerRunnerSettings != null)
		{
			remoteConnection.setUseSockets(debuggerRunnerSettings.getTransport() == DebuggerSettings.SOCKET_TRANSPORT);
			remoteConnection.setAddress(debuggerRunnerSettings.getDebugPort());
		}

		return remoteConnection;
	}

	@Override
	public GenericDebuggerRunnerSettings createConfigurationData(ConfigurationInfoProvider settingsProvider)
	{
		return new GenericDebuggerRunnerSettings();
	}

	@Override
	public void patch(OwnJavaParameters javaParameters, RunnerSettings settings, RunProfile runProfile, final boolean beforeExecution) throws ExecutionException
	{
		doPatch(javaParameters, settings);
		runCustomPatchers(javaParameters, Executor.EP_NAME.findExtension(DefaultDebugExecutor.class), runProfile);
	}

	private static RemoteConnection doPatch(final OwnJavaParameters javaParameters, final RunnerSettings settings) throws ExecutionException
	{
		final GenericDebuggerRunnerSettings debuggerSettings = ((GenericDebuggerRunnerSettings) settings);
		if(StringUtil.isEmpty(debuggerSettings.getDebugPort()))
		{
			debuggerSettings.setDebugPort(DebuggerUtils.getInstance().findAvailableDebugAddress(debuggerSettings.getTransport()).address());
		}
		return DebuggerManagerImpl.createDebugParameters(javaParameters, debuggerSettings, false);
	}

	@Override
	public SettingsEditor<GenericDebuggerRunnerSettings> getSettingsEditor(final Executor executor, RunConfiguration configuration)
	{
		if(configuration instanceof RunConfigurationWithRunnerSettings)
		{
			if(((RunConfigurationWithRunnerSettings) configuration).isSettingsNeeded())
			{
				return new GenericDebuggerParametersRunnerConfigurable(configuration.getProject());
			}
		}
		return null;
	}
}

@@ -3797,33 +3797,16 @@
 ;%0A%09%7D%0A%0A%09@
-javax.annotation.
 Nullable
@@ -5195,116 +5195,8 @@
 ));%0A
-%09%09%09%09sessionImpl.addExtraStopActions(((DefaultExecutionResult) executionResult).getAdditionalStopActions());%0A
 %09%09%09%7D

drop unused method usage