/*
 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */


import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.net.*;

/*
 * The base class for HTTP and SOCKS servers.
 */
public abstract class AbstractHttpSockServer extends Thread {

    private List<ConnectionChecker> connectionCheckers = new ArrayList<>();
    private List<DataChecker> dataCheckers = new ArrayList<>();
    private int port;
    protected int delay = 0;

    public AbstractHttpSockServer(int port) {
        this.port = port;
    }

    public void setDelay(int connectionDelay) {
        this.delay = connectionDelay;
    }

    public void addConnectionChecker(ConnectionChecker socks4ConnectionChecker) {
        connectionCheckers.add(socks4ConnectionChecker);
    }

    public void addDataChecker(DataChecker socks4DataChecker) {
        dataCheckers.add(socks4DataChecker);
    }

    public List<ConnectionChecker> getConnectionCheckers() {
        return connectionCheckers;
    }

    public List<DataChecker> getDataCheckers() {
        return dataCheckers;
    }

    @Override
    public void run()  {
        printStartMessage();

        // create server socket
        ServerSocket socket;
        try {
            socket = new ServerSocket(port,50,InetAddress.getLoopbackAddress());
        } catch (IOException e) {
            System.out.println("Cannot create server socket" +  e);
            return;
        }


        // the main server loop that accepts client connections
        while (!Thread.interrupted()) {
            try {
                Socket connectionSocket = socket.accept();
                System.out.println("Client connection accepted");
                if (connectionSocket != null) {
                    // process the client connection in new thread
                    Thread clientThread = new Thread(createClientConnectionHandler(connectionSocket));
                    clientThread.start();
                }
            } catch (IOException e) {
                System.out.println("Cannot accept client connection" + e);
            }
        }
    }

    public int getPort() {
        return port;
    }

    /*
     * The factory method for creation of entity that processes client connections.
     * @param socket Client socket
     * @return Entity that processes the client connection
     */
    protected abstract Runnable createClientConnectionHandler(Socket socket);

    /*
     * This method should be overridden for printing some info when server starts.
     */
    protected abstract void printStartMessage();
}
