package org.jetbrains.plugins.groovy.debugger;

import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ModuleRunProfile;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.JavaProgramPatcher;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import consulo.container.boot.ContainerPathManager;
import consulo.groovy.module.extension.GroovyModuleExtension;
import consulo.java.execution.configurations.OwnJavaParameters;
import consulo.java.module.extension.JavaModuleExtension;
import consulo.platform.Platform;
import org.jetbrains.plugins.groovy.GroovyFileTypeLoader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.regex.Pattern;


/**
 * @author peter
 */
public class GroovyHotSwapper extends JavaProgramPatcher
{
	private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.debugger.GroovyHotSwapper");
	private static final String GROOVY_HOTSWAP_AGENT_PATH = "groovy.hotswap.agent.path";

	private static final Pattern SPRING_LOADED_PATTERN = Pattern.compile("-javaagent:.+springloaded-core-[^/\\\\]+\\.jar");

	private static boolean endsWithAny(String s, List<String> endings)
	{
		for(String extension : endings)
		{
			if(s.endsWith(extension))
			{
				return true;
			}
		}
		return false;
	}

	private static boolean containsGroovyClasses(Project project)
	{
		final List<String> extensions = new ArrayList<String>();
		for(String extension : GroovyFileTypeLoader.getAllGroovyExtensions())
		{
			extensions.add("." + extension);
		}
		final GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
		for(String fileName : FilenameIndex.getAllFilenames(project))
		{
			if(endsWithAny(fileName, extensions))
			{
				if(!FilenameIndex.getVirtualFilesByName(project, fileName, scope).isEmpty())
				{
					return true;
				}
			}
		}
		return false;
	}

	private static boolean hasSpringLoadedReloader(OwnJavaParameters javaParameters)
	{
		for(String param : javaParameters.getVMParametersList().getParameters())
		{
			if(SPRING_LOADED_PATTERN.matcher(param).matches())
			{
				return true;
			}
		}

		return false;
	}

	public void patchJavaParameters(Executor executor, RunProfile configuration, OwnJavaParameters javaParameters)
	{
		if(!executor.getId().equals(DefaultDebugExecutor.EXECUTOR_ID))
		{
			return;
		}

		if(!GroovyDebuggerSettings.getInstance().ENABLE_GROOVY_HOTSWAP)
		{
			return;
		}

		if(hasSpringLoadedReloader(javaParameters))
		{
			return;
		}

		if(!(configuration instanceof RunConfiguration))
		{
			return;
		}

		final Project project = ((RunConfiguration) configuration).getProject();
		if(project == null)
		{
			return;
		}

		if(configuration instanceof ModuleRunProfile)
		{
			Module[] modulesInConfiguration = ((ModuleRunProfile) configuration).getModules();
			final Module module = modulesInConfiguration.length == 0 ? null : modulesInConfiguration[0];
			if(module != null)
			{
				final JavaModuleExtension extension = ModuleUtilCore.getExtension(module, JavaModuleExtension.class);
				final LanguageLevel level = extension == null ? null : extension.getLanguageLevel();
				if(level != null && !level.isAtLeast(LanguageLevel.JDK_1_5))
				{
					return;
				}

				if(ModuleUtilCore.getExtension(module, GroovyModuleExtension.class) == null)
				{
					return;
				}
			}
		}

		Sdk jdk = javaParameters.getJdk();
		if(jdk != null)
		{
			String vendor = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VENDOR);
			if(vendor != null && vendor.contains("IBM"))
			{
				LOG.info("Due to IBM JDK pecularities (IDEA-59070) we don't add groovy agent when running applications under it");
				return;
			}
		}

		if(!project.isDefault() && containsGroovyClasses(project))
		{
			final String agentPath = handleSpacesInPath(getAgentJarPath());
			if(agentPath != null)
			{
				javaParameters.getVMParametersList().add("-javaagent:" + agentPath);
			}
		}
	}

	@javax.annotation.Nullable
	private static String handleSpacesInPath(String agentPath)
	{
		if(agentPath.contains(" "))
		{
			final File dir = new File(ContainerPathManager.get().getSystemPath(), "groovyHotSwap");
			if(dir.getAbsolutePath().contains(" "))
			{
				LOG.info("Groovy hot-swap not used since the agent path contains spaces: " + agentPath + "\n" +
						"One can move the agent to a directory with no spaces in path and specify its path in <Consulo dist>/bin/idea.properties as " + GROOVY_HOTSWAP_AGENT_PATH + "=<path>");
				return null;
			}

			final File toFile = new File(dir, "gragent.jar");
			try
			{
				FileUtil.copy(new File(agentPath), toFile);
				return toFile.getPath();
			}
			catch(IOException e)
			{
				LOG.info(e);
			}
		}
		return agentPath;
	}

	private static String getAgentJarPath()
	{
		final String userDefined = Platform.current().getRuntimeProperty(GROOVY_HOTSWAP_AGENT_PATH);
		if(userDefined != null && new File(userDefined).exists())
		{
			return userDefined;
		}

		return FileUtil.toSystemDependentName(new File(PluginManager.getPluginPath(GroovyHotSwapper.class), "gragent.jar").getPath());
	}
}

@@ -388,102 +388,8 @@
 er;%0A
-import com.intellij.ide.plugins.PluginManager;%0Aimport com.intellij.openapi.diagnostic.Logger;%0A
 impo
@@ -423,24 +423,24 @@
 ule.Module;%0A
+
 import com.i
@@ -853,24 +853,71 @@
 athManager;%0A
+import consulo.container.plugin.PluginManager;%0A
 import consu
@@ -1080,32 +1080,63 @@
 oduleExtension;%0A
+import consulo.logging.Logger;%0A
 import consulo.p
@@ -1521,48 +1521,8 @@
 nce(
-%22#org.jetbrains.plugins.groovy.debugger.
 Groo
@@ -1533,17 +1533,22 @@
 tSwapper
-%22
+.class
 );%0A%09priv
@@ -5455,16 +5455,22 @@
 rrent().
+jvm().
 getRunti

drop usage of deprecated methods