package sinetja;

import io.netty.handler.codec.http.cors.CorsConfig;
import io.netty.handler.codec.http.router.DualMethodRouter;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;

import java.io.File;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLException;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.CharsetUtil;

public class Server extends DualMethodRouter<Action, Server> {
  @Override protected Server getThis() { return this; }

  //----------------------------------------------------------------------------

  /** 1MB */
  public static final int MAX_CONTENT_LENGTH = 1024 * 1024;

  //----------------------------------------------------------------------------
  // Config

  private SslContext sslContext = null;

  /** Default: MAX_CONTENT_LENGTH */
  private int maxContentLength = MAX_CONTENT_LENGTH;

  /** Default: UTF-8. */
  private Charset charset = CharsetUtil.UTF_8;

  private CorsConfig cors;

  private Object before;

  private Object after;

  private Object error;

  //----------------------------------------------------------------------------

  public SslContext sslContext() {
    return sslContext;
  }

  public Server sslContext(SslContext sslContext) {
    this.sslContext = sslContext;
    return getThis();
  }

  /** Uses autogenerated selfsigned certificate. */
  public Server jdkSsl() throws SSLException, CertificateException {
    SelfSignedCertificate cert = new SelfSignedCertificate();
    sslContext = SslContext.newServerContext(SslProvider.JDK, cert.certificate(), cert.privateKey());
    return getThis();
  }

  public Server jdkSsl(String certChainFile, String keyFile) throws SSLException {
    sslContext = SslContext.newServerContext(SslProvider.JDK, new File(certChainFile), new File(keyFile));
    return getThis();
  }

  public Server jdkSsl(String certChainFile, String keyFile, String keyPassword) throws SSLException {
    sslContext = SslContext.newServerContext(SslProvider.JDK, new File(certChainFile), new File(keyFile), keyPassword);
    return getThis();
  }

  public Server jdkSsl(
    String certChainFile, String keyFile, String keyPassword,
    Iterable<String> ciphers, Iterable<String> nextProtocols, long sessionCacheSize, long sessionTimeout
  ) throws SSLException {
    sslContext = SslContext.newServerContext(
      SslProvider.JDK, new File(certChainFile), new File(keyFile), keyPassword,
      ciphers, nextProtocols, sessionCacheSize, sessionTimeout
    );
    return getThis();
  }

  /** Uses autogenerated selfsigned certificate. */
  public Server openSsl() throws SSLException, CertificateException {
    SelfSignedCertificate cert = new SelfSignedCertificate();
    sslContext = SslContext.newServerContext(SslProvider.OPENSSL, cert.certificate(), cert.privateKey());
    return getThis();
  }

  public Server openSsl(String certChainFile, String keyFile) throws SSLException {
    sslContext = SslContext.newServerContext(SslProvider.OPENSSL, new File(certChainFile), new File(keyFile));
    return getThis();
  }

  public Server openSsl(String certChainFile, String keyFile, String keyPassword) throws SSLException {
    sslContext = SslContext.newServerContext(SslProvider.OPENSSL, new File(certChainFile), new File(keyFile), keyPassword);
    return getThis();
  }

  public Server openSsl(
    String certChainFile, String keyFile, String keyPassword,
    Iterable<String> ciphers, Iterable<String> nextProtocols, long sessionCacheSize, long sessionTimeout
  ) throws SSLException {
    sslContext = SslContext.newServerContext(
      SslProvider.OPENSSL, new File(certChainFile), new File(keyFile), keyPassword,
      ciphers, nextProtocols, sessionCacheSize, sessionTimeout
    );
    return getThis();
  }

  //----------------------------------------------------------------------------

  public int maxContentLength() {
    return maxContentLength;
  }

  public Server maxContentLength(int maxContentLength) {
    this.maxContentLength = maxContentLength;
    return getThis();
  }

  //----------------------------------------------------------------------------

  public Charset charset() {
    return charset;
  }

  public Server charset(Charset charset) {
    this.charset = charset;
    return getThis();
  }

  //----------------------------------------------------------------------------

  public CorsConfig cors() {
    return cors;
  }

  public Server cors(CorsConfig cors) {
    this.cors = cors;
    return getThis();
  }

  //----------------------------------------------------------------------------

  public Object before() {
    return before;
  }

  public Server before(Action before) {
    this.before = before;
    return getThis();
  }

  public Server before(Class<? extends Action> before) {
    this.before = before;
    return getThis();
  }

  public Object after() {
    return after;
  }

  public Server after(Action after) {
    this.after = after;
    return getThis();
  }

  public Server after(Class<? extends Action> after) {
    this.after = after;
    return getThis();
  }

  //----------------------------------------------------------------------------

  public Object error() {
    return error;
  }

  public Server error(ErrorHandler error) {
    this.error = error;
    return getThis();
  }

  public Server error(Class<? extends ErrorHandler> error) {
    this.error = error;
    return getThis();
  }

  //----------------------------------------------------------------------------

  protected final PipelineInitializer pipelineInitializer = new PipelineInitializer(this);

  public void start(int port) {
    NioEventLoopGroup bossGroup   = new NioEventLoopGroup(1);
    NioEventLoopGroup workerGroup = new NioEventLoopGroup();

    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
       .childOption(ChannelOption.TCP_NODELAY,  java.lang.Boolean.TRUE)
       .childOption(ChannelOption.SO_KEEPALIVE, java.lang.Boolean.TRUE)
       .channel(NioServerSocketChannel.class)
       .childHandler(pipelineInitializer);

      Channel ch = b.bind(port).sync().channel();
      if (sslContext == null)
        Log.info("HTTP server started: http://127.0.0.1:" + port + '/');
      else
        Log.info("HTTP server started: https://127.0.0.1:" + port + '/');

      ch.closeFuture().sync();
    } catch (InterruptedException e) {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }
}

@@ -6544,32 +6544,33 @@
   Log.info(%22HTTP
+S
  server started:

Change "HTTP server started: https://..." to "HTTPS server started: https://..."