package org.holer.server.container;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import java.util.Collection;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.holer.server.constant.HolerCode;
import org.holer.server.init.ExtraClientInitializer;
import org.holer.server.init.ServerInitializer;
import org.holer.server.model.HolerChannel;
import org.holer.server.model.HolerPort;
import org.holer.server.model.HolerResult;
import org.holer.server.util.ServerMgr;
import org.holer.server.util.ServerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public class ServerContainer
{
  private static final Logger log = LoggerFactory.getLogger(ServerContainer.class);

  private static ServerContainer container = null;
  private NioEventLoopGroup serverWorker;
  private NioEventLoopGroup serverBoss;
  private ServerBootstrap strap;


  public ServerContainer() {
    this.serverWorker = null;
    this.serverBoss = null;
    this.strap = null;
    init();
  }

  public static ServerContainer getContainer() {
    if (null == container) {
      container = new ServerContainer();
    }
    return container;
  }

  
  public ServerBootstrap getBootstrap() {
    if (null == this.strap) {
      this.strap = new ServerBootstrap();
      ((ServerBootstrap)this.strap.group(this.serverBoss, this.serverWorker).channel(io.netty.channel.socket.nio.NioServerSocketChannel.class)).childHandler(new ExtraClientInitializer());
    } 
    return this.strap;
  }


  public void listenPort(HolerPort hp) {
    if (!hp.getEnabled() || hp.getExpireAt() <= System.currentTimeMillis()) {
      ServerMgr.unbind(hp.getPortNum()); //如果端口已经过期，则取消端口绑定
      return;
    } 
    ServerBootstrap strap = getBootstrap();
    ServerMgr.bind(strap, hp.getPortNum());
  }


  public void listenPorts() {
    Collection<HolerChannel> channels = ServerMgr.holerChannels().values();
    if (CollectionUtils.isEmpty(channels)) {
      return;
    }
    HolerResult<?> result = ServerUtil.verifyLicense();
    result.setCode(1000);
    if (HolerCode.OK.value() != result.getCode()) {
      log.warn(result.getMsg());
    }
    
    ServerBootstrap strap = getBootstrap();
    for (HolerChannel hc : channels) {
      if (null == hc.getClient() || MapUtils.isEmpty(hc.getPorts())) {
        continue;
      }
      if (!hc.getClient().getEnabled()) {
        continue;
      }
      for (HolerPort hp : hc.getPorts().values()) {
        if (!hp.getEnabled() || hp.getExpireAt() <= System.currentTimeMillis()) {
          continue;
        }
        ServerMgr.bind(strap, hp.getPortNum());
        log.info("绑定公共端口：" + hp.getPortNum());
      } 
    }
  }


  
  private ServerBootstrap newServer(boolean sslEnabled) {
    ServerBootstrap strap = new ServerBootstrap();
    ((ServerBootstrap)strap.group(this.serverBoss, this.serverWorker)
      .channel(io.netty.channel.socket.nio.NioServerSocketChannel.class))
      .childHandler(new ServerInitializer(sslEnabled));
    return strap;
  }

  
  public void init() {
    this.serverWorker = new NioEventLoopGroup();
    this.serverBoss = new NioEventLoopGroup();
    ServerMgr.initCfg();
    ServerMgr.initHoler();
  }


  
  public void start() {
    try {
      int servPort = ServerMgr.getServerPort();
      String servHost = ServerMgr.getServerHost();
      ServerBootstrap server = newServer(false);
      server.bind(servHost, servPort).get(); //绑定端口
      log.info("Holer服务器在主机上启动 [{}:{}]", servHost, servPort);
      if (ServerMgr.isSslServerEnable()) { //判断是否启动SSL服务器
        ServerBootstrap sslServer = newServer(true);
        ChannelFuture cf = sslServer.bind(ServerMgr.getSslServerHost(), ServerMgr.getSslServerPort());
        cf.sync();
        log.info("Holer SSL服务器在主机上启动 [{}:{}]", ServerMgr.getSslServerHost(), ServerMgr.getSslServerPort());
      }
    
    } catch (Exception e) {
      log.error("在初始化服务器时捕获中断异常！", e);
      throw new RuntimeException(e);
    } 
    listenPorts();
  }

  
  public void stop() {
    this.serverBoss.shutdownGracefully();
    this.serverWorker.shutdownGracefully();
  }
}
