package org.chenyang.http.impl.bootstrap;

import org.chenyang.http.ExceptionLogger;
import org.chenyang.http.HttpConnectionFactory;
import org.chenyang.http.HttpServerConnection;
import org.chenyang.http.config.SocketConfig;
import org.chenyang.http.impl.DefaultBHttpServerConnection;
import org.chenyang.http.protocol.HttpService;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocket;

public class HttpServer {
   private final int port;
   private final InetAddress ifAddress;
   private final SocketConfig socketConfig;
   private final ServerSocketFactory serverSocketFactory;
   private final HttpService httpService;
   private final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
   private final SSLServerSetupHandler sslSetupHandler;
   private final ExceptionLogger exceptionLogger;
   private final ThreadPoolExecutor listenerExecutorService;
   private final ThreadGroup workerThreads;
   private final WorkerPoolExecutor workerExecutorService;
   private final AtomicReference<Status> status;
   private volatile ServerSocket serverSocket;
   private volatile RequestListener requestListener;

   HttpServer(int port, InetAddress ifAddress, SocketConfig socketConfig, ServerSocketFactory serverSocketFactory, HttpService httpService, HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory, SSLServerSetupHandler sslSetupHandler, ExceptionLogger exceptionLogger) {
      this.port = port;
      this.ifAddress = ifAddress;
      this.socketConfig = socketConfig;
      this.serverSocketFactory = serverSocketFactory;
      this.httpService = httpService;
      this.connectionFactory = connectionFactory;
      this.sslSetupHandler = sslSetupHandler;
      this.exceptionLogger = exceptionLogger;
      this.listenerExecutorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue(), new ThreadFactoryImpl("HTTP-listener-" + this.port));
      this.workerThreads = new ThreadGroup("HTTP-workers");
      this.workerExecutorService = new WorkerPoolExecutor(0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactoryImpl("HTTP-worker", this.workerThreads));
      this.status = new AtomicReference(Status.READY);
   }

   public InetAddress getInetAddress() {
      ServerSocket localSocket = this.serverSocket;
      return localSocket != null ? localSocket.getInetAddress() : null;
   }

   public int getLocalPort() {
      ServerSocket localSocket = this.serverSocket;
      return localSocket != null ? localSocket.getLocalPort() : -1;
   }

   public void start() throws IOException {
      if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
         this.serverSocket = this.serverSocketFactory.createServerSocket(this.port, this.socketConfig.getBacklogSize(), this.ifAddress);
         this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
         if (this.socketConfig.getRcvBufSize() > 0) {
            this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
         }

         if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
            this.sslSetupHandler.initialize((SSLServerSocket)this.serverSocket);
         }

         this.requestListener = new RequestListener(this.socketConfig, this.serverSocket, this.httpService, this.connectionFactory, this.exceptionLogger, this.workerExecutorService);
         this.listenerExecutorService.execute(this.requestListener);
      }

   }

   public void stop() {
      if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
         this.listenerExecutorService.shutdown();
         this.workerExecutorService.shutdown();
         RequestListener local = this.requestListener;
         if (local != null) {
            try {
               local.terminate();
            } catch (IOException ex) {
               this.exceptionLogger.log(ex);
            }
         }

         this.workerThreads.interrupt();
      }

   }

   public void awaitTermination(long timeout, TimeUnit timeUnit) throws InterruptedException {
      this.workerExecutorService.awaitTermination(timeout, timeUnit);
   }

   public void shutdown(long gracePeriod, TimeUnit timeUnit) {
      this.stop();
      if (gracePeriod > 0L) {
         try {
            this.awaitTermination(gracePeriod, timeUnit);
         } catch (InterruptedException var10) {
            Thread.currentThread().interrupt();
         }
      }

      for(Worker worker : this.workerExecutorService.getWorkers()) {
         HttpServerConnection conn = worker.getConnection();

         try {
            conn.shutdown();
         } catch (IOException ex) {
            this.exceptionLogger.log(ex);
         }
      }

   }

   static enum Status {
      READY,
      ACTIVE,
      STOPPING;
   }
}
