package org.jboss.windup.graph.rexster;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.container.simple.lifecycle.SimpleContainer;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.listeners.AfterGraphInitializationListener;
import org.jboss.windup.graph.listeners.BeforeGraphCloseListener;

import com.thinkaurelius.titan.core.TitanGraph;
import com.tinkerpop.blueprints.util.wrappers.event.EventGraph;
import com.tinkerpop.frames.FramedGraph;
import com.tinkerpop.rexster.protocol.EngineConfiguration;
import com.tinkerpop.rexster.protocol.EngineController;
import com.tinkerpop.rexster.server.DefaultRexsterApplication;
import com.tinkerpop.rexster.server.HttpRexsterServer;
import com.tinkerpop.rexster.server.RexProRexsterServer;
import com.tinkerpop.rexster.server.RexsterProperties;

public class RexsterInitializer implements AfterGraphInitializationListener, BeforeGraphCloseListener
{
    private String rexsterExtractDirectory;
    private Map<String, Object> configuration;
    private static final Logger log = Logger.getLogger(RexsterInitializer.class.getName());
    boolean started = false;
    private RexProRexsterServer rexPro;
    private HttpRexsterServer rexsterServer;

    public RexsterInitializer()
    {
    }

    private Addon getAddon()
    {
        Set<Addon> addons = SimpleContainer.getFurnace(RexsterInitializer.class.getClassLoader()).getAddonRegistry().getAddons();
        for (Addon addon : addons)
        {
            boolean isRexster = addon.getId().getName().contains("rexster");
            if (isRexster)
            {
                return addon;
            }
        }
        return null;
    }

    public void start(FramedGraph<EventGraph<TitanGraph>> graph)
    {
        try (PrintWriter out = new PrintWriter("rexster.xml"))
        {
            String path = getClass().getResource("/public").getPath();
            if (path.contains("!"))
            {
                path = path.split("!")[0];
            }
            if (path.contains(":"))
            {
                path = path.split(":")[1];
            }
            if (path.endsWith(".jar"))
            {
                File rexsterAddonDir = new File(path);

                new File(rexsterExtractDirectory).mkdirs();
                extractJarFile(rexsterAddonDir, rexsterExtractDirectory);
            }
            else
            {
                // remove the "public" from the end
                String substring = path.substring(0, path.length() - 8);
                rexsterExtractDirectory = substring;
            }
            out.println(createRexsterXmlFileString(configuration));
            out.flush();
            RexsterProperties properties = new RexsterProperties("rexster.xml");
            configureScriptEngine(properties);
            rexsterServer = new HttpRexsterServer(properties);
            rexsterServer.start(new DefaultRexsterApplication("main", graph.getBaseGraph()));

            rexPro = new RexProRexsterServer(properties, true);
            rexPro.start(new DefaultRexsterApplication("main", graph));
            started = true;

        }
        catch (Exception e)
        {
            log.severe("Error while creating rexster.xml - more details: " + e.getLocalizedMessage());
            e.printStackTrace();
        }
    }

    public boolean isStarted()
    {
        return started;
    }

    private void extractJarFile(File jarFile, String destinationDirectory)
    {
        byte[] buffer = new byte[1024];
        try( ZipInputStream zis = new ZipInputStream(new FileInputStream(jarFile)) )
        {
            // create output directory is not exists
            File folder = new File(destinationDirectory);
            if (!folder.exists())
            {
                folder.mkdir();
            }

            // get the zipped file list entry
            ZipEntry ze = null;
            while ( (ze = zis.getNextEntry()) != null)
            {

                String fileName = ze.getName();
                File newFile = new File(destinationDirectory + File.separator + fileName);

                // create all non exists folders
                // else you will hit FileNotFoundException for compressed folder
                new File(newFile.getParent()).mkdirs();

                FileOutputStream fos = new FileOutputStream(newFile);

                int len;
                while ((len = zis.read(buffer)) > 0)
                {
                    fos.write(buffer, 0, len);
                }
                fos.close();
                ze = zis.getNextEntry();
            }
            zis.closeEntry();
        }
        catch (IOException ex)
        {
            log.severe("Exception while extracting jar file -> " + jarFile.getAbsolutePath() + " : " + ex.getLocalizedMessage());
        }
    }

    private void configureScriptEngine(RexsterProperties properties)
    {
        // the EngineController needs to be configured statically before requests start serving so that it can
        // properly construct ScriptEngine objects with the correct reset policy. allow scriptengines to be
        // configured so that folks can drop in different gremlin flavors.
        final List<EngineConfiguration> configuredScriptEngines = new ArrayList<>();
        final List<HierarchicalConfiguration> configs = properties.getScriptEngines();
        for (HierarchicalConfiguration config : configs)
        {
            configuredScriptEngines.add(new EngineConfiguration(config));
        }

        EngineController.configure(configuredScriptEngines);
    }

    private String createRexsterXmlFileString(Map<String, Object> conf)
    {
        String fileString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<rexster>\n" +
                    "  <http>\n" +
                    "    <server-port>8182</server-port>\n" +
                    "    <server-host>0.0.0.0</server-host>\n" +
                    "    <base-uri>http://localhost</base-uri>\n" +
                    "    <web-root>" + rexsterExtractDirectory + "/public</web-root>\n" +
                    // "    <http.web-root> </http.web-root> \n" +
                    "    <character-set>UTF-8</character-set>\n" +
                    "    <enable-jmx>false</enable-jmx>\n" +
                    "    <enable-doghouse>true</enable-doghouse>\n" +
                    "    <max-post-size>2097152</max-post-size>\n" +
                    "    <max-header-size>8192</max-header-size>\n" +
                    "    <upload-timeout-millis>30000</upload-timeout-millis>\n" +
                    "    <thread-pool>\n" +
                    "      <worker>\n" +
                    "        <core-size>8</core-size>\n" +
                    "        <max-size>8</max-size>\n" +
                    "      </worker>\n" +
                    "      <kernal>\n" +
                    "        <core-size>4</core-size>\n" +
                    "        <max-size>4</max-size>\n" +
                    "      </kernal>\n" +
                    "    </thread-pool>\n" +
                    "    <io-strategy>leader-follower</io-strategy>\n" +
                    "  </http>\n" +
                    "  <rexpro>\n" +
                    "    <server-port>8184</server-port>\n" +
                    "    <server-host>0.0.0.0</server-host>\n" +
                    "    <session-max-idle>1790000</session-max-idle>\n" +
                    "    <session-check-interval>3000000</session-check-interval>\n" +
                    "    <enable-jmx>false</enable-jmx>\n" +
                    "    <read-buffer>65536</read-buffer>\n" +
                    "    <thread-pool>\n" +
                    "      <worker>\n" +
                    "          <core-size>8</core-size>\n" +
                    "        <max-size>8</max-size>\n" +
                    "      </worker>\n" +
                    "      <kernal>\n" +
                    "        <core-size>4</core-size>\n" +
                    "        <max-size>4</max-size>\n" +
                    "      </kernal>\n" +
                    "    </thread-pool>\n" +
                    "    <io-strategy>leader-follower</io-strategy>\n" +
                    "  </rexpro>\n" +
                    "  <shutdown-port>8183</shutdown-port>\n" +
                    "  <shutdown-host>127.0.0.1</shutdown-host>\n" +
                    "  <config-check-interval>10000</config-check-interval>\n" +
                    "  <script-engines>\n" +
                    "    <script-engine>\n" +
                    "      <name>gremlin-groovy</name>\n" +
                    "      <reset-threshold>-1</reset-threshold>\n" +
                    "      <init-scripts>config/init.groovy</init-scripts>\n" +
                    "      <imports>com.tinkerpop.rexster.client.*</imports>\n" +
                    "      <static-imports>java.lang.Math.PI</static-imports>\n" +
                    "    </script-engine>\n" +
                    "  </script-engines>\n" +
                    "  <security>\n" +
                    "    <authentication>\n" +
                    "        <type>default</type>\n" +
                    "        <configuration>\n" +
                    "          <users>\n" +
                    "            <user>\n" +
                    "              <username>rexster</username>\n" +
                    "              <password>rexster</password>\n" +
                    "            </user>\n" +
                    "          </users>\n" +
                    "        </configuration>\n" +
                    "    </authentication>\n" +
                    "  </security>\n" +
                    "    <metrics>\n" +
                    "        <reporter>\n" +
                    "            <type>jmx</type>\n" +
                    "        </reporter>\n" +
                    "        <reporter>\n" +
                    "            <type>http</type>\n" +
                    "        </reporter>\n" +
                    "        <reporter>\n" +
                    "            <type>console</type>\n" +
                    "            <properties>\n" +
                    "                <rates-time-unit>SECONDS</rates-time-unit>\n" +
                    "                <duration-time-unit>SECONDS</duration-time-unit>\n" +
                    "                <report-period>10</report-period>\n" +
                    "                <report-time-unit>MINUTES</report-time-unit>\n" +
                    "                <includes>http.rest.*</includes>\n" +
                    "                <excludes>http.rest.*.delete</excludes>\n" +
                    "            </properties>\n" +
                    "        </reporter>\n" +
                    "    </metrics>\n" +
                    "  <graphs>\n" +
                    "<graph>\n" +
                    "    <graph-name>titan</graph-name>\n" +
                    "    <graph-type>com.thinkaurelius.titan.tinkerpop.rexster.TitanGraphConfiguration</graph-type>\n" +
                    "    <graph-location> " + conf.get("storage.directory") + "</graph-location>\n" +
                    "    <graph-read-only>false</graph-read-only>\n" +
                    "    <properties>\n" +
                    "        <storage.backend>" + conf.get("storage.backend") + "</storage.backend>\n" +
                    "        <storage.directory>" + conf.get("storage.directory") + "</storage.directory>\n" +
                    "        <index.search.backend>" + conf.get("index.search.backend")
                    + "</index.search.backend>\n" +
                    "        <index.search.directory> " + conf.get("index.search.directory")
                    + "</index.search.directory>\n" +
                    "    </properties>\n" +
                    "    <extensions>\n" +
                    "        <allows>\n" +
                    "            <allow>tp:gremlin</allow>\n" +
                    "        </allows>\n" +
                    "    </extensions>\n" +
                    " </graph>" +
                    "  </graphs>\n" +
                    "</rexster>";
        return fileString;

    }

    @Override
    public void afterGraphStarted(Map<String, Object> configuration, GraphContext graphContext)
    {
        this.configuration = configuration;
        rexsterExtractDirectory = getAddon().getRepository().getAddonDescriptor(getAddon().getId()).getParent() + "/rexster-extract";
        start(graphContext.getFramed());
    }

    @Override
    public void beforeGraphClose()
    {
        try
        {
            if(rexPro !=null) {
               rexPro.stop();
            }
            if(rexsterServer !=null) {
                rexsterServer.stop();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
