package com.redhat.ceylon.compiler.java.runtime.model;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jboss.modules.ModuleClassLoader;

import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.impl.JULLogger;
import com.redhat.ceylon.compiler.loader.impl.reflect.CachedTOCJars;
import com.redhat.ceylon.compiler.loader.impl.reflect.ReflectionModelLoader;
import com.redhat.ceylon.compiler.loader.impl.reflect.mirror.ReflectionClass;
import com.redhat.ceylon.compiler.loader.impl.reflect.mirror.ReflectionUtils;
import com.redhat.ceylon.compiler.loader.mirror.ClassMirror;
import com.redhat.ceylon.compiler.loader.model.LazyModule;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.Modules;
import com.redhat.ceylon.compiler.typechecker.model.Unit;

public class RuntimeModelLoader extends ReflectionModelLoader {

    private Map<Module,ClassLoader> classLoaders = new HashMap<Module,ClassLoader>();
    private Map<String, Module> moduleCache = new HashMap<String, Module>();
    private CachedTOCJars jars = new CachedTOCJars();

    public RuntimeModelLoader(ModuleManager moduleManager, Modules modules) {
        super(moduleManager, modules, new JULLogger());
    }

    @Override
    protected List<String> getPackageList(Module module, String packageName) {
        return jars.getPackageList(packageName);
    }

    @Override
    protected boolean packageExists(Module module, String packageName) {
        return jars.packageExists(packageName);
    }

    @Override
    protected Class<?> loadClass(Module module, String name) {
        ClassLoader classLoader = classLoaders.get(module);
        if(classLoader == null)
            return null;
        try{
            return classLoader.loadClass(name);
        }catch(ClassNotFoundException x){
            return null;
        }
    }

    @Override
    public void addModuleToClassPath(Module module, ArtifactResult artifact) {
        String cacheKey = cacheKeyByModule(module.getNameAsString(), module.getVersion());
        moduleCache.put(cacheKey, module);
        if(artifact == null)
            return;
        File file = artifact.artifact();
        if(file == null)
            return;
        jars.addJar(file);
        if(module instanceof LazyModule){
            ((LazyModule) module).loadPackageList(artifact);
        }
    }

    public void addModuleClassLoader(Module module, ClassLoader classLoader) {
        classLoaders.put(module, classLoader);
    }

    @Override
    protected Module findModuleForClassMirror(ClassMirror classMirror) {
        Class<?> klass = ((ReflectionClass)classMirror).klass;
        return findModuleForClass(klass);
    }

    public Module findModuleForClass(Class<?> klass){
        ClassLoader cl = klass.getClassLoader();
        if(cl instanceof ModuleClassLoader){
            ModuleClassLoader classLoader = (ModuleClassLoader)cl;
            org.jboss.modules.Module jbossModule = classLoader.getModule();
            String name = jbossModule.getIdentifier().getName();
            String version = jbossModule.getIdentifier().getSlot();
            String cacheKey = cacheKeyByModule(name, version);
            return moduleCache.get(cacheKey);
        }else{
            // revert to a single classloader version?
            // FIXME: perhaps we can have other one-classloader-to-one-module setups than jboss modules?
            String pkgName = ReflectionUtils.getPackageName(klass);
            return lookupModuleInternal(pkgName);
        }
    }
    
    private String cacheKeyByModule(String name, String version) {
        if(name.equals(Module.DEFAULT_MODULE_NAME))
            return name + "/"; // no version
        return name + "/" + version;
    }

    public Unit getUnit() {
        return typeFactory;
    }

}

@@ -236,16 +236,59 @@
 Result;%0A
+import com.redhat.ceylon.cmr.api.JDKUtils;%0A
 import c
@@ -320,24 +320,24 @@
 .JULLogger;%0A
-
 import com.r
@@ -1871,32 +1871,485 @@
 sLoader == null)
+%7B%0A            if(JDKUtils.isJDKModule(module.getNameAsString())%0A                    %7C%7C JDKUtils.isOracleJDKModule(module.getNameAsString()))%7B%0A                // the JDK does not have class loaders so load it from the root class loader%0A                try %7B%0A                    return ClassLoader.getSystemClassLoader().loadClass(name);%0A                %7D catch (ClassNotFoundException e) %7B%0A                    return null;%0A                %7D%0A            %7D
 %0A            ret
@@ -2350,32 +2350,42 @@
    return null;%0A
+        %7D%0A
         try%7B%0A   
@@ -4062,16 +4062,16 @@
 odules?%0A
-
         
@@ -4126,24 +4126,365 @@
 ame(klass);%0A
+            // even on jboss modules, the jdk has no class loader, so jdk classes will have to be resolved%0A            // to the jdk modules by package%0A            String jdkModuleName = JDKUtils.getJDKModuleNameForPackage(pkgName);%0A            if(jdkModuleName != null)%0A                return findModule(jdkModuleName, JDK_MODULE_VERSION);%0A
             

fix for ceylon/ceylon-compiler#1990