package org.javacs;

import com.sun.tools.javac.code.Symbol;
import io.typefox.lsapi.*;
import javax.tools.*;
import com.sun.tools.javac.tree.*;
import org.junit.Ignore;
import org.junit.Test;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;

public class SymbolIndexTest {
    private static final Logger LOG = Logger.getLogger("main");

    @Test
    public void all() {
        Set<String> all = search("");

        assertThat(all, not(empty()));
    }

    @Test
    public void searchClasses() {
        Set<String> all = search("JLanguageServer");

        assertThat(all, hasItem("JavaLanguageServer"));
    }

    @Test
    public void searchMethods() {
        Set<String> all = search("gTextDocumentService");

        assertThat(all, hasItem("getTextDocumentService"));
    }

    @Test
    public void referenceConstructor() {
        String path = "/org/javacs/example/ReferenceConstructor.java";
        int line = 2;
        int character = 22;

        Symbol classSymbol = symbol(path, line, character);
        List<Integer> references = index.references(classSymbol).map(ref -> ref.getRange().getStart().getLine()).collect(Collectors.toList());

        // Constructor reference on line 8
        assertThat(references, hasItem(8));
    }

    private Symbol symbol(String path, int line, int character) {
        JavaFileObject file = new GetResourceFileObject(path);
        JCTree.JCCompilationUnit tree = compiler.parse(file);

        compiler.compile(tree);
        index.update(tree, compiler.context);

        long offset = JavaLanguageServer.findOffset(file, line, character);
        SymbolUnderCursorVisitor visitor = new SymbolUnderCursorVisitor(file, offset, compiler.context);

        tree.accept(visitor);

        return visitor.found.get();
    }

    private Set<String> search(String query) {
        return index.search(query).map(s -> s.getName()).collect(Collectors.toSet());
    }

    private SymbolIndex index = getIndex();
    
    private static SymbolIndex getIndex() {
        try {
            Set<Path> classPath = new HashSet<>();

            for (String line : Files.readAllLines(Paths.get("classpath.txt"))) {
                for (String entry : line.split(File.pathSeparator)) {
                    classPath.add(Paths.get(entry).toAbsolutePath());
                }
            }
            Set<Path> sourcePath = Collections.singleton(Paths.get("src/main/java").toAbsolutePath());
            Path outputDirectory = Paths.get("out").toAbsolutePath();
            SymbolIndex index = new SymbolIndex(classPath, sourcePath, outputDirectory, (paths, errs) -> {
                errs.getDiagnostics().forEach(d -> LOG.info(d.getMessage(Locale.US)));
            });

            index.initialIndexComplete.join();

            return index;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static JavacHolder compiler = newCompiler();

    private static JavacHolder newCompiler() {
        return new JavacHolder(Collections.emptySet(),
                               Collections.singleton(Paths.get("src/test/resources")),
                               Paths.get("out"));
    }
}
@@ -1393,16 +1393,57 @@
 sSymbol)
+%0A                                        
 .map(ref
@@ -1478,24 +1478,65 @@
 ).getLine())
+%0A                                        
 .collect(Col
@@ -1657,221 +1657,870 @@
     
-private Symbol symbol(String path, int line, int character) %7B%0A        JavaFileObject file = new GetResourceFileObject(path);%0A        JCTree.JCCompilationUnit tree = compiler.parse(file);%0A%0A        co
+@Test%0A    public void symbolsInFile() %7B%0A        String path = %22/org/javacs/example/AutocompleteMember.java%22;%0A%0A        compile(path);%0A%0A        List%3CString%3E all = index.allInFile(new GetResourceFileObject(path).toUri())%0A                                .map(s -%3E s.getName())%0A                                .collect(Collectors.toList());%0A%0A        assertThat(all, hasItems(%22methodStatic%22, %22method%22,%0A                                 %22methodStaticPrivate%22, %22methodPrivate%22));%0A%0A        assertThat(all, hasItems(%22fieldStatic%22, %22field%22,%0A                                 %22fieldStaticPrivate%22, %22fieldPrivate%22));%0A%0A        // TODO%0A        // assertThat(%22excludes implicit constructor%22, all, not(hasItems(%22AutocompleteMember%22)));%0A    %7D%0A%0A    @Test%0A    public void explicitConstructor() %7B%0A        String path = %22/org/javacs/exa
 mp
-i
 le
-r.
+/ReferenceConstructor.java%22;%0A%0A        
 compile(
 tree
@@ -2519,23 +2519,24 @@
 ile(
-tree
+path
 );%0A
+%0A
         
 inde
@@ -2535,47 +2535,422 @@
     
-index.update(tree, compiler.context
+List%3CString%3E all = index.allInFile(new GetResourceFileObject(path).toUri())%0A                                .map(s -%3E s.getName())%0A                                .collect(Collectors.toList());%0A%0A        assertThat(%22includes explicit constructor%22, all, hasItem(%22ReferenceConstructor%22));%0A    %7D%0A%0A    private Symbol symbol(String path, int line, int character) %7B%0A        JCTree.JCCompilationUnit tree = compile(path
 );%0A
-%0A
     
@@ -2997,20 +2997,36 @@
 dOffset(
-f
+tree.getSourceF
 ile
+()
 , line, 
@@ -3109,20 +3109,36 @@
 Visitor(
-f
+tree.getSourceF
 ile
+()
 , offset
@@ -3225,32 +3225,324 @@
 d.get();%0A    %7D%0A%0A
+    private JCTree.JCCompilationUnit compile(String path) %7B%0A        JavaFileObject file = new GetResourceFileObject(path);%0A        JCTree.JCCompilationUnit tree = compiler.parse(file);%0A%0A        compiler.compile(tree);%0A        index.update(tree, compiler.context);%0A        return tree;%0A    %7D%0A%0A
     private Set%3C

Test find-symbol-in-file