package org.jetbrains.kotlin.core.model;

import java.net.URI;
import java.net.URISyntaxException;

import org.eclipse.core.internal.resources.Resource;
import org.eclipse.core.internal.resources.ResourceInfo;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.internal.core.ExternalFoldersManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.core.filesystem.KotlinFileSystem;
import org.jetbrains.kotlin.core.log.KotlinLogger;
import org.jetbrains.kotlin.core.utils.ProjectUtils;

public class KotlinJavaManager {
    public static final KotlinJavaManager INSTANCE = new KotlinJavaManager();
    
    public static final Path KOTLIN_BIN_FOLDER = new Path("kotlin_bin");
    private static final IClasspathEntry KOTLIN_BIN_CLASSPATH_ENTRY = new ClasspathEntry(IPackageFragmentRoot.K_BINARY,
            IClasspathEntry.CPE_LIBRARY,
            KOTLIN_BIN_FOLDER,
            ClasspathEntry.INCLUDE_ALL,
            ClasspathEntry.EXCLUDE_NONE,
            null,
            null,
            null,
            false,
            ClasspathEntry.NO_ACCESS_RULES,
            false,
            ClasspathEntry.NO_EXTRA_ATTRIBUTES);
    
    private KotlinJavaManager() {
    }
    
    public void registerKtExternalBinFolder(@NotNull IJavaProject javaProject) {
        if (!hasKotlinBinFolder(javaProject)) {
            addFolderForKotlinClassFiles(javaProject);
        }
        
        updateKotlinBinFolderFileSystem(javaProject);
    }
    
    @NotNull
    public IPath getKotlinBinFullPath(@NotNull IProject project) {
        return project.getLocation().append(KOTLIN_BIN_FOLDER);
    }
    
    @NotNull
    public IFolder getKotlinBinFolderFor(@NotNull IProject project) {
        return project.getFolder(KOTLIN_BIN_FOLDER);
    }
    
    private void addFolderForKotlinClassFiles(@NotNull IJavaProject javaProject) { 
        try {
            if (!hasKotlinBinFolder(javaProject)) {
                IFolder folder = javaProject.getProject().getFolder(KOTLIN_BIN_FOLDER);
                if (!folder.exists()) {
                    folder.create(true, true, null);
                }
                
                ProjectUtils.addToClasspath(javaProject, KOTLIN_BIN_CLASSPATH_ENTRY);
            }
        } catch (JavaModelException e) {
            KotlinLogger.logAndThrow(e);
        } catch (CoreException e) {
            KotlinLogger.logAndThrow(e);
        }
    }
    
    private void updateKotlinBinFolderFileSystem(@NotNull IJavaProject javaProject) {
        try {
            IProject externalProject = ExternalFoldersManager.getExternalFoldersManager().getExternalFoldersProject();
            if (!externalProject.exists()) {
                return;
            }
            
            IPath ktBinPath = getKotlinBinFullPath(javaProject.getProject());
            for (IResource member : externalProject.members()) {
                if (ktBinPath.equals(member.getLocation())) {
                    setKtFileSystemFor(member);
                    return;
                }
            }
        } catch (CoreException e) {
            KotlinLogger.logAndThrow(e);
        }
    }
    
    private boolean hasKotlinBinFolder(@NotNull final IJavaProject javaProject) {
        try {
            if (!javaProject.getProject().getFolder(KOTLIN_BIN_FOLDER).exists()) {
                return false;
            }
            
            for (IClasspathEntry cp : javaProject.getRawClasspath()) {
                if (KOTLIN_BIN_FOLDER.equals(cp.getPath().removeFirstSegments(1))) {
                    return true;
                }
            }
        } catch (JavaModelException e) {
            KotlinLogger.logAndThrow(e);
        }
        
        return false;
    }
    
    private void setKtFileSystemFor(IResource resource) {
        ResourceInfo resourceInfo = ((Resource) resource).getResourceInfo(true, false);
        Workspace workspace = (Workspace) ResourcesPlugin.getWorkspace();
        workspace.getFileSystemManager().setLocation(
                resource, 
                resourceInfo, 
                setKtURIFor(resource.getLocationURI()));
    }
    
    private URI setKtURIFor(URI locationURI) {
        try {
            return new URI(
                    KotlinFileSystem.SCHEME, 
                    locationURI.getUserInfo(), 
                    locationURI.getHost(), 
                    locationURI.getPort(), 
                    locationURI.getPath(), 
                    locationURI.getQuery(), 
                    locationURI.getFragment());
        } catch (URISyntaxException e) {
            KotlinLogger.logAndThrow(e);
            throw new IllegalStateException(e);
        }
    }
}

@@ -806,69 +806,8 @@
 ry;%0A
-import org.eclipse.jdt.internal.core.ExternalFoldersManager;%0A
 impo
@@ -844,16 +844,16 @@
 otNull;%0A
+
 import o
@@ -1919,22 +1919,38 @@
         
-update
+setKtFileSystemFor(get
 KotlinBi
@@ -1957,25 +1957,18 @@
 nFolderF
-ileSystem
+or
 (javaPro
@@ -1967,24 +1967,38 @@
 (javaProject
+.getProject())
 );%0A    %7D%0A   
@@ -2800,32 +2800,32 @@
 lException e) %7B%0A
+
             Kotl
@@ -2951,734 +2951,8 @@
    %0A
-    private void updateKotlinBinFolderFileSystem(@NotNull IJavaProject javaProject) %7B%0A        try %7B%0A            IProject externalProject = ExternalFoldersManager.getExternalFoldersManager().getExternalFoldersProject();%0A            if (!externalProject.exists()) %7B%0A                return;%0A            %7D%0A            %0A            IPath ktBinPath = getKotlinBinFullPath(javaProject.getProject());%0A            for (IResource member : externalProject.members()) %7B%0A                if (ktBinPath.equals(member.getLocation())) %7B%0A                    setKtFileSystemFor(member);%0A                    return;%0A                %7D%0A            %7D%0A        %7D catch (CoreException e) %7B%0A            KotlinLogger.logAndThrow(e);%0A        %7D%0A    %7D%0A    %0A
     

Fix integration with Java due to change kotlin_bin classpath: now Kotlin light classes are stored in class folder, not external class folder