package kalang.compiler.compile;

import kalang.compiler.ast.ClassNode;
import kalang.compiler.util.AstUtil;
import kalang.compiler.util.NameUtil;

import javax.annotation.Nullable;
import java.util.*;

/**
 *
 * @author Kason Yang
 */
public class TypeNameResolver {

    private Map<String, String> simpleToFullNames = new HashMap();

    private List<String> importPackages = new LinkedList();

    private AstLoader astLoader;

    public TypeNameResolver() {
    }

    public void importClass(String className){
        this.importClass(className,NameUtil.getSimpleClassName(className));
    }
    
    public void importClass(String className,String alias) {
        this.simpleToFullNames.put(alias , className);
    }

    public void importPackage(String packageName) {
        this.importPackages.add(packageName);
    }

    /**
     * 
     * @param id
     * @param topClass
     * @param declaringClass
     * @return full name of type,null if failed.
     */
    @Nullable
    public String resolve(String id,ClassNode topClass, ClassNode declaringClass) {
        if(id.contains(".")) return id;
        if (simpleToFullNames.containsKey(id)) {
            return simpleToFullNames.get(id);
        } else if (id.contains("$")){
            String[] idParts = id.split("\\$");
            String outerClassName = this.resolve(idParts[0],topClass,declaringClass);
            if (outerClassName==null){
                return null;
            }
            return outerClassName + "$" + idParts[1];
        }else {
            String[] innerClassesNames = AstUtil.listInnerClassesNames(topClass, true);
            String path = declaringClass.name;
            while(!path.isEmpty()) {
                String name = path + "$" + id;
                for(String c:innerClassesNames) {
                    if (name.equals(c)) {
                        return c;
                    }
                }
                int last$ = path.lastIndexOf('$');
                path = last$ > 0 ? path.substring(0,last$) : "";
            }
            List<String> paths = new ArrayList<>(importPackages.size() + 1);
            paths.addAll(importPackages);
            paths.add(NameUtil.getPackageName(topClass.name));
            int pathSize = paths.size();
            for (int i=pathSize-1;i>=0;i--) {
                String p = paths.get(i);
                String clsName;
                if (p != null && p.length() > 0) {
                    clsName = p + "." + id;
                } else {
                    clsName = id;
                }
                if (astLoader!=null &&  astLoader.getAst(clsName) != null) {
                    return clsName;
                }
            }
        }
        return null;
    }

    public void setAstLoader(AstLoader astLoader) {
        this.astLoader = astLoader;
    }
    
}

@@ -425,16 +425,91 @@
 oader;%0A%0A
+    private Map%3CString,Optional%3CString%3E%3E resolvedIdMap = new HashMap%3C%3E();%0A%0A
     publ
@@ -1137,24 +1137,626 @@
 ingClass) %7B%0A
+        String key = String.format(%22%25s%7C%25s%7C%25s%22,id,topClass.name,declaringClass.name);%0A        Optional%3CString%3E val = resolvedIdMap.get(key);%0A        if (val==null) %7B%0A            String className = doResolve(id, topClass, declaringClass);%0A            val = className == null ? Optional.empty() : Optional.of(className);%0A            resolvedIdMap.put(key,val);%0A        %7D%0A        return val.orElse(null);%0A    %7D%0A%0A    public void setAstLoader(AstLoader astLoader) %7B%0A        this.astLoader = astLoader;%0A    %7D%0A%0A    @Nullable%0A    public String doResolve(String id,ClassNode topClass,ClassNode declaringClass) %7B%0A
         if(i
@@ -3416,103 +3416,8 @@
    %7D
-%0A%0A    public void setAstLoader(AstLoader astLoader) %7B%0A        this.astLoader = astLoader;%0A    %7D
 %0A   

optimize TypeNameResolver