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.protocol.HttpService;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;

class RequestListener implements Runnable {
   private final SocketConfig socketConfig;
   private final ServerSocket serversocket;
   private final HttpService httpService;
   private final HttpConnectionFactory<? extends HttpServerConnection> connectionFactory;
   private final ExceptionLogger exceptionLogger;
   private final ExecutorService executorService;
   private final AtomicBoolean terminated;

   public RequestListener(SocketConfig socketConfig, ServerSocket serversocket, HttpService httpService, HttpConnectionFactory<? extends HttpServerConnection> connectionFactory, ExceptionLogger exceptionLogger, ExecutorService executorService) {
      this.socketConfig = socketConfig;
      this.serversocket = serversocket;
      this.connectionFactory = connectionFactory;
      this.httpService = httpService;
      this.exceptionLogger = exceptionLogger;
      this.executorService = executorService;
      this.terminated = new AtomicBoolean(false);
   }

   public void run() {
      try {
         while(!this.isTerminated() && !Thread.interrupted()) {
            Socket socket = this.serversocket.accept();
            socket.setSoTimeout(this.socketConfig.getSoTimeout());
            socket.setKeepAlive(this.socketConfig.isSoKeepAlive());
            socket.setTcpNoDelay(this.socketConfig.isTcpNoDelay());
            if (this.socketConfig.getRcvBufSize() > 0) {
               socket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
            }

            if (this.socketConfig.getSndBufSize() > 0) {
               socket.setSendBufferSize(this.socketConfig.getSndBufSize());
            }

            if (this.socketConfig.getSoLinger() >= 0) {
               socket.setSoLinger(true, this.socketConfig.getSoLinger());
            }

            HttpServerConnection conn = this.connectionFactory.createConnection(socket);
            Worker worker = new Worker(this.httpService, conn, this.exceptionLogger);
            this.executorService.execute(worker);
         }
      } catch (Exception ex) {
         this.exceptionLogger.log(ex);
      }

   }

   public boolean isTerminated() {
      return this.terminated.get();
   }

   public void terminate() throws IOException {
      if (this.terminated.compareAndSet(false, true)) {
         this.serversocket.close();
      }

   }
}
