
/*
 *  Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
*/
package oracle.sample.resource;

import groovy.lang.GroovyShell;
import oracle.sample.DefiningClassLoader;
import oracle.sample.StringList;
import org.codehaus.groovy.control.CompilationFailedException;

import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * Provides root resource.
 */
@Path("/")
public class AppResource {


    private static Logger logger = Logger.getLogger(AppResource.class.getName());
    PrintWriter pr = new PrintWriter(System.out, true);
    static String urls = "#import google collection\n" +

            "http://repo1.maven.org/maven2/com/google/guava/guava/11.0/guava-11.0.pom\n\n#Please enter URLs pointing maven POMs or jars\n\n" +
            "#Example1 Direct POM  // Default repo will be used for loading the artifacts\n" +
            "#http://repo1.maven.org/maven2/com/google/guava/guava/11.0/guava-11.0.pom\n" +
            "\n" +
            "#Example2 Maven repo,POM   // All the artifacts will be downloaded from the given repo\n" +
            "#http://repo1.maven.org/maven2,http://repo1.maven.org/maven2/com/google/guava/guava/11.0/guava-11.0.pom\n" +
            "\n" +
            "#Example3 Direct JAR from remote\n" +
            "#http://myhost:port/my.jar\n\n" +

            "#Example4 Direct JAR from local\n" +
            "#file:/customer/scratch/my.jar";

    static DefiningClassLoader classLoader = null;


    static {
        try {
            new AppResource().createLoader(urls);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // static String language = null;


    private synchronized List<String> getAvailableLanguages() throws Exception {
        if (classLoader == null) {
            throw new Exception("No Loader is created.");
        }
        try {
            ScriptEngineManager manager = new ScriptEngineManager(classLoader);

            List<String> engines = new ArrayList<String>();

            List<ScriptEngineFactory> factories = manager.getEngineFactories();
            for (ScriptEngineFactory fact : factories) {
                engines.add(fact.getLanguageName());
            }
            return engines;
        } finally {

        }
    }


    @POST
    @Path("createloader")
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public StringList createLoader(String script) {
        try {
            urls = script;
            String[] urls = script.split("\n");

                classLoader = new DefiningClassLoader(AppResource.class.getClassLoader());
                for (String u : urls) {
                    u = u.trim();
                    if (u.startsWith("#")) {
                        continue;
                    }
                    if (u.toLowerCase().endsWith(".pom")) {
                        if (u.contains(",")) {
                            int com = u.indexOf(",");
                            classLoader.addPom(u.substring(0, com), u.substring(com + 1, u.length()));

                        } else {
                            classLoader.addPom(u);
                        }
                    } else if (u.toLowerCase().endsWith(".jar")) {
                        classLoader.addUrl(u);
                    } else {
                        // classLoader.addUrl("Unsupported://" + u);
                    }
                }

            //List<String> engines = getAvailableLanguages();
//            if (engines.isEmpty()) {
//                language = null;
//            }
//
//            if (language == null) {
//                if (!engines.isEmpty()) {
//                    language = engines.get(0);
//                }
//            }
//
//            if (language != null) {
//                engines.remove(language);
//                engines.add(0, language);
//            }

            return new StringList(classLoader.getAllUrls());
        } catch (Throwable t1) {
            throw new WebApplicationException(t1, handleError(t1));
        }
    }

    @POST
    @Path("changelanguage")
    public Response changeLanguage(String newLanguage) {
        //  language = newLanguage;
        return Response.status(202).build();
    }


    @GET
    @Path("libraries")
    @Produces({MediaType.TEXT_PLAIN})
    public Response getLibraries(String newLanguage) {
        //  language = newLanguage;
        return Response.status(200).type(MediaType.TEXT_PLAIN).entity(urls).build();
    }


    @Path("cli/unix4j/{dir:.*}")
    public SSHResource ssh(@PathParam("dir")String dir) {
          return new SSHResource(dir);
    }

     //Execute
    @POST
    @Path("execute")
    public Response executeScript(String script,@Context HttpServletRequest request) {
        try {
            if (classLoader == null) {
                throw new Exception("No classloader is created.");
            }

//            ScriptEngineManager manager = new ScriptEngineManager();

            GroovyShell engine = new GroovyShell(classLoader);
            engine.getContext().setVariable("httprequest", request);
            Object obj = engine.evaluate(script);
            return Response.status(200).type(MediaType.TEXT_PLAIN).entity(obj == null ? "null" : obj.toString()).build();
        } catch (Throwable t1) {
            return handleError(t1);
        } finally {

        }
    }

    private Response handleError(Throwable t1) {
        PrintWriter pr = new PrintWriter(System.out, true);
        String message = t1.getMessage();
        if (message == null) {
            message = "No message raised ...";
        }
        try {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            PrintWriter pw = new PrintWriter(bo, true);
            t1.printStackTrace(pw);
            pw.flush();
            String error = new String(bo.toByteArray(), "UTF-8");
            //    String[] content = error.split("\n", 2);
            ///  String errorMsg = content[0];
            // String errorTrace = content[1];
            int lineNum = 1;
            int column = 1;
            if (ScriptException.class.isAssignableFrom(t1.getClass())) {
                lineNum = ((ScriptException) t1).getLineNumber();
                column = ((ScriptException) t1).getColumnNumber();
            }
            pr.println("line number " + lineNum + ":" + column);
            if (CompilationFailedException.class.isAssignableFrom(t1.getClass())) {

                //pr.println(message);
                int index = message.indexOf("@ line ");
                if (index >= 0) {
                    int numberstart = index + 7;
                    int indexComma = message.indexOf(",", numberstart);
                    if (indexComma >= 0) {
                        String lineText = message.substring(numberstart, indexComma);
                        try {
                            //pr.println("Line:" + lineText);
                            lineNum = Integer.parseInt(lineText);
                        } catch (NumberFormatException nfe) {
                            nfe.printStackTrace();
                        }
                    }
                }

            }
            StackTraceElement[] trace = t1.getStackTrace();

            for (int i = trace.length - 1; i >= 0; i--) {
                if (trace[i].getClassName().equals("Script1")) {
                    if (CompilationFailedException.class.isAssignableFrom(t1.getClass())) {
                        //To account inner scripts if any ...
                        lineNum += (trace[i].getLineNumber() - 1);
                    } else {
                        lineNum = trace[i].getLineNumber();
                    }
                    break;
                }
            }

            // pr.println("Line last:" + lineNum);
            return Response.status(500).type(MediaType.TEXT_PLAIN).entity(error).header("GROOVY_SCRIPT_ERROR_LINE_NUMBER", lineNum).header("GROOVY_SCRIPT_ERROR", message.replaceAll("\n", " ")).build();
        } catch (Exception e) {
            e.printStackTrace(pr);
            return Response.status(500).type(MediaType.TEXT_PLAIN).entity(e.getMessage()).build();
        }
    }

    @POST
    @Path("compile")
    public Response compileScript(String script) {

        try {
            if (classLoader == null) {
                throw new Exception("No Classloader is set.");
            }

//            ScriptEngineManager manager = new ScriptEngineManager();
//            Thread.currentThread().setContextClassLoader(classLoader);
//
//            ScriptEngine engine = manager.getEngineByName(language);
//            if (Compilable.class.isAssignableFrom(engine.getClass())) {
//                Compilable comp = (Compilable) engine;
//                comp.compile(script);
//            } else {
//                throw new Exception("Script Engine " + language + " does not support script compilation");
//            }

            GroovyShell engine = new GroovyShell(classLoader);
            engine.parse(script);

            return Response.status(200).type(MediaType.TEXT_PLAIN_TYPE).entity("").build();
        } catch (Throwable t1) {

            return handleError(t1);
        } finally {

        }
    }
}
