/*
Copyright 2007-2009 WebDriver committers
Copyright 2007-2009 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package org.openqa.selenium.environment.webserver;

import junit.framework.Assert;

import javax.servlet.Servlet;
import javax.servlet.Filter;
import java.io.File;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSocketConnector;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.servlets.MultiPartFilter;
import org.openqa.selenium.networkutils.NetworkUtils;

import static org.openqa.selenium.internal.PortProber.findFreePort;

public class Jetty7AppServer implements AppServer {

  private static final String DEFAULT_CONTEXT_PATH = "/common";
  private static final String JS_SRC_CONTEXT_PATH = "/js/src";
  private static final String JS_TEST_CONTEXT_PATH = "/js/test";
  private static final String THIRD_PARTY_JS_CONTEXT_PATH =
      "/third_party/closure/goog";

  private static final NetworkUtils networkUtils =  new NetworkUtils();

  private int port;
  private int securePort;
  private File path;
  private File jsSrcRoot;
  private File jsTestRoot;
  private File thirdPartyJsRoot;
  private final Server server;
  private WebAppContext context;
  private ContextHandlerCollection handlers;
  private final String hostName;

  public Jetty7AppServer() {
    this(networkUtils.getNonLoopbackAddressOfThisMachine());
  }
  
  public Jetty7AppServer(String hostName) {
    this.hostName = hostName;
    // Be quiet. Unless we want things to be chatty
    if (!Boolean.getBoolean("webdriver.debug")) {
      new NullLogger().disableLogging();
    }

    server = new Server();

    path = findRootOfWebApp();
    jsSrcRoot = findJsSrcWebAppRoot();
    jsTestRoot = findJsTestWebAppRoot();
    thirdPartyJsRoot = findThirdPartyJsWebAppRoot();

    handlers = new ContextHandlerCollection();

    context = addWebApplication(DEFAULT_CONTEXT_PATH, path);
    addWebApplication(JS_SRC_CONTEXT_PATH, jsSrcRoot);
    addWebApplication(JS_TEST_CONTEXT_PATH, jsTestRoot);
    addWebApplication(THIRD_PARTY_JS_CONTEXT_PATH, thirdPartyJsRoot);

    server.setHandler(handlers);

    addServlet("Redirecter", "/redirect", RedirectServlet.class);
    addServlet("InfinitePagerServer", "/page/*", PageServlet.class);
    addServlet("Manifest", "/manifest/*", ManifestServlet.class);
    addServlet("Uploader", "/upload", UploadServlet.class);
    addServlet("Unusual encoding", "/encoding", EncodingServlet.class);
    addServlet("Sleeper", "/sleep", SleepingServlet.class);
    addFilter(MultiPartFilter.class, "/upload", 0 /* DEFAULT dispatches */);

    listenOn(findFreePort());
    listenSecurelyOn(findFreePort());
  }

  public File getJsTestRoot() {
    return jsTestRoot;
  }

  protected File findRootOfWebApp() {
    // TODO(simonstewart): replace this with InProject.locate()
    String[] possiblePaths = {
        "common/src/web",
        "../common/src/web",
        "../../common/src/web",
    };

    File current;
    for (String potential : possiblePaths) {
      current = new File(potential);
      if (current.exists()) {
        return current;
      }
    }

    Assert.assertTrue("Unable to find common web files. These are located in the common directory",
                      path.exists());
    return null;
  }

  private static File findJsSrcWebAppRoot() {
    return findWebAppRoot(new String[] {
        "common/src/js",
        "../common/src/js",
        "../../common/src/js"
    });
  }

  private static File findJsTestWebAppRoot() {
    return findWebAppRoot(new String[] {
        "common/test/js",
        "../common/test/js",
        "../../common/test/js"
    });
  }

  private static File findThirdPartyJsWebAppRoot() {
    return findWebAppRoot(new String[] {
        "third_party/closure/goog",
        "../third_party/closure/goog",
        "../../third_party/closure/goog"
    });
  }

  private static File findWebAppRoot(String[] possiblePaths) {
    for (String potential : possiblePaths) {
      File current = new File(potential);
      if (current.exists()) {
        return current;
      }
    }
    return null;
  }

  public String getHostName() {
    return hostName;
  }

  public String getAlternateHostName() {
    return networkUtils.getPrivateLocalAddress();
  }

  public String whereIs(String relativeUrl) {
    if (!relativeUrl.startsWith("/")) {
      relativeUrl = DEFAULT_CONTEXT_PATH + "/" + relativeUrl;
    }
    return "http://" + getHostName() + ":" + port + relativeUrl;
  }

  public String whereElseIs(String relativeUrl) {
    if (!relativeUrl.startsWith("/")) {
      relativeUrl = DEFAULT_CONTEXT_PATH + "/" + relativeUrl;
    }
    return "http://" + getAlternateHostName() + ":" + port + relativeUrl;
  }

  public String whereIsSecure(String relativeUrl) {
    if (!relativeUrl.startsWith("/")) {
      relativeUrl = DEFAULT_CONTEXT_PATH + "/" + relativeUrl;
    }
    return "https://" + getHostName() + ":" + securePort + relativeUrl;
  }

  public void start() {
    SelectChannelConnector connector = new SelectChannelConnector();
    connector.setPort(port);
    server.addConnector(connector);

    File keyStore = getKeyStore();
    if (!keyStore.exists()) {
      throw new RuntimeException(
          "Cannot find keystore for SSL cert: " + keyStore.getAbsolutePath());
    }

    SslSocketConnector secureSocket = new SslSocketConnector();
    secureSocket.setPort(securePort);
    secureSocket.setKeystore(keyStore.getAbsolutePath());
    secureSocket.setPassword("password");
    secureSocket.setKeyPassword("password");
    secureSocket.setTruststore(keyStore.getAbsolutePath());
    secureSocket.setTrustPassword("password");
    server.addConnector(secureSocket);

    try {
      server.start();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  protected File getKeyStore() {
    return new File(findRootOfWebApp(), "../../test/java/keystore");
  }

  public void listenOn(int port) {
    this.port = port;
  }

  public void listenSecurelyOn(int port) {
    this.securePort = port;
  }


  protected void addListener(Connector listener) {
    server.addConnector(listener);
  }

  public void stop() {
    try {
      server.stop();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void addServlet(String name, String url, Class<? extends Servlet> servletClass) {
    try {
      context.addServlet(servletClass, url);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void addFilter(Class<? extends Filter> filter, String path, int dispatches) {
    context.addFilter(filter, path, dispatches);
  }

  private WebAppContext addWebApplication(String contextPath, File rootDir) {
    return addWebApplication(contextPath, rootDir.getAbsolutePath());
  }

  private WebAppContext addWebApplication(String contextPath, String absolutePath) {
    WebAppContext app = new WebAppContext();
    app.setContextPath(contextPath);
    app.setWar(absolutePath);
    handlers.addHandler(app);
    return app;
  }

  public void addAdditionalWebApplication(String context, String absolutePath) {
    addWebApplication(context, absolutePath);
  }

  public static void main(String[] args) {
    Jetty7AppServer server = new Jetty7AppServer("localhost");
    server.port = 2310;
    server.start();
  }
}

@@ -2061,57 +2061,19 @@
 his(
-networkUtils.getNonLoopbackAddressOfThisMachine()
+%22localhost%22
 );%0D%0A

Revert hostname change in the JettyXAppServer made in 10150.