package de.dhbwstuttgart.languageServerInterface;


import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.exceptions.CompilerWarning;
import de.dhbwstuttgart.exceptions.WarningsException;
import de.dhbwstuttgart.languageServerInterface.model.LanguageServerTransferObject;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.target.tree.TargetStructure;
import de.dhbwstuttgart.util.Logger;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Implementation of an Interface for the Language-Server to get the Resultset and abstract Syntax.
 */
public class LanguageServerInterface {


    public LanguageServerTransferObject getResultSetAndAbstractSyntax(String path, String resetNamesTo) throws IOException, URISyntaxException, ClassNotFoundException {
        NameGenerator.resetTo(resetNamesTo);
        return getResultSetAndAbstractSyntax(path);

    }

    public SourceFile getAst(String path, String resetNamesTo) throws IOException, URISyntaxException, ClassNotFoundException {
        NameGenerator.resetTo(resetNamesTo);
        return getAST(path);
    }


    /**
     * returns the ResultSets, GenericResultSet and the AST
     * You have to give the input as well as the path because of potential locks when the File is currently opened in an IDE.
     * Example: file:///c:/test/main.jav -> file:///c:/test/out/main.class
     *
     * @param pathAsString the URI of the File. See Example.
     */
    public LanguageServerTransferObject getResultSetAndAbstractSyntax(String pathAsString){
        System.setOut(new PrintStream(OutputStream.nullOutputStream()));
        ConsoleInterface.logLevel = Logger.LogLevel.DEBUG;
        ConsoleInterface.writeLogFiles = true;
        ConsoleInterface.writeToConsole = false;
        Logger.resetLogs();

        try {
            var uri = new URI(pathAsString);
            var path = Path.of(uri);
            var file = path.toFile();
            Files.createDirectories(path.getParent().resolve("out"));
            var compiler = new JavaTXCompiler(List.of(file), List.of(path.getParent().toFile()), path.getParent().resolve("out").toFile(), true);
            compiler.parseAll();

            List<CompilerWarning> warnings = List.of();
            var parsedSource = compiler.sourceFiles.get(file);
            var tiResults = compiler.typeInference(file);
            var classFiles = compiler.generateBytecode(parsedSource, tiResults);

            try {
                compiler.checkWarnings();
                compiler.writeClassFile(classFiles, file);
            } catch(WarningsException e) {
                warnings = e.compilerWarnings;
            }

            return new LanguageServerTransferObject(parsedSource, "", compiler.lastConstraints, compiler.getGeneratedGenerics(), warnings);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * returns the AST without calculating the result
     * You have to give the input as well as the path because of potential locks when the File is currently opened in an IDE.
     * Example: file:///c:/test/main.jav -> file:///c:/test/out/main.class
     *
     * @param path the URI of the File. See Example.
     * @throws IOException
     * @throws ClassNotFoundException
     * @throws URISyntaxException
     */
    public SourceFile getAST(String path) throws IOException, ClassNotFoundException, URISyntaxException {


        System.setOut(new PrintStream(OutputStream.nullOutputStream()));


        URI uri = new URI(path);
        ArrayList<String> pathWithoutName = new ArrayList<>(List.of(uri.getPath().split("/")));
        pathWithoutName.remove(List.of(uri.getPath().split("/")).size() - 1);
        String stringPathWithoutName = "";

        for (String i : pathWithoutName) {
            stringPathWithoutName += "/" + i;
        }

        try {
            FileUtils.cleanDirectory(new File(stringPathWithoutName + "/out"));
        } catch (Exception e) {

        }
        try {
            (new File(stringPathWithoutName + "/out")).mkdirs();
        } catch (Exception e) {

        }
        var test = getAST(uri.getPath().split("/")[uri.getPath().split("/").length - 1], new File(stringPathWithoutName).getPath());
        System.setOut(System.out);
        return test;
    }

    public static SourceFile getAST(String filename, String filePath) throws IOException, ClassNotFoundException {
        var file = Path.of(filePath, filename).toFile();
        var compiler = new JavaTXCompiler(List.of(file), List.of(file.getParentFile()), Path.of(filePath + "/out").toFile(), false);
        return compiler.sourceFiles.get(file);
    }
}
