package com.lts.cmd;

import com.lts.core.logger.Logger;
import com.lts.core.logger.LoggerFactory;

import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 主要用于 curl
 *
 * @author Robert HG (254963746@qq.com) on 10/26/15.
 */
public class HttpCmdServer {

    private final Logger LOGGER = LoggerFactory.getLogger(HttpCmdServer.class);

    private final AtomicBoolean start = new AtomicBoolean(false);
    private HttpCmdAcceptor acceptor;
    private int port;
    private String bindAddr;
    private HttpCmdContext context;
    private int portFindTimes;

    private HttpCmdServer(String bindAddr, int port) {
        this.port = port > 0 ? port : 8719;
        this.bindAddr = bindAddr;
        this.context = new HttpCmdContext();
    }

    public void start() throws HttpCmdException {
        try {
            if (start.compareAndSet(false, true)) {
                // 开启监听命令
                acceptor = new HttpCmdAcceptor(getServerSocket(), context);
                acceptor.start();
                LOGGER.info("Start succeed at port {}", port);
            }
        } catch (Exception t) {
            LOGGER.error("Start error at port {}", port, t);
            throw new HttpCmdException(t);
        }
    }

    private ServerSocket getServerSocket() throws IOException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port, 100);
            serverSocket.setReuseAddress(true);
        } catch (BindException e) {
            port = port + 1;
            serverSocket = getServerSocket();
            if (portFindTimes++ > 50) {
                throw e;
            }
        }
        return serverSocket;
    }

    public void stop() {
        if (acceptor != null) {
            acceptor.stop();
        }
    }

    public int getPort() {
        return port;
    }

    public void registerCommand(HttpCmdProc proc) {
        context.addCmdProcessor(proc);
    }

    public void registerCommands(HttpCmdProc... procs) {
        if (procs != null && procs.length > 0) {
            for (HttpCmdProc proc : procs) {
                this.registerCommand(proc);
            }
        }
    }

    /**
     * 保证一个jvm公用一个 HttpCmdServer
     */
    public static class Factory {

        private static HttpCmdServer httpCmdServer;

        public static HttpCmdServer getHttpCmdServer(String bindAddr, int port) {
            if (httpCmdServer != null) {
                return httpCmdServer;
            }
            synchronized (Factory.class) {
                if (httpCmdServer != null) {
                    return httpCmdServer;
                }
                httpCmdServer = new HttpCmdServer(bindAddr, port);
                return httpCmdServer;
            }
        }
    }

}
