package com.neverwinterdp.zookeeper.tool.server;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.zookeeper.server.DatadirCleanupManager;
import org.apache.zookeeper.server.ServerConfig;
import org.apache.zookeeper.server.ZooKeeperServerMain;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
import org.apache.zookeeper.server.quorum.QuorumPeerMain;

import com.neverwinterdp.tool.server.Server;
import com.neverwinterdp.util.JSONSerializer;
import com.neverwinterdp.util.io.FileUtil;
/**
 * @author Tuan Nguyen
 * @email tuan08@gmail.com
 */
public class EmbededZKServer implements Server {
  private ZookeeperLaucher launcher;
  private Thread zkThread;
  private Properties zkProperties = new Properties();
  private int port;

  public EmbededZKServer(Map<String, String> overrideProperties) {
    init(overrideProperties);
  }

  public EmbededZKServer(String dataDir, int port) {
    Map<String, String> props = new HashMap<String, String>();
    props.put("tickTime", "2000");
    props.put("initLimit","5");
    props.put("syncLimit","5");
    props.put("dataDir", dataDir);
    this.port = port;
    props.put("clientPort", Integer.toString(port));
    init(props);
  }
  
  public EmbededZKServer(String dataDir) {
    this(dataDir, 2181);
  }
  
  void init(Map<String, String> overrideProperties) {
    zkProperties.put("dataDir", "./build/data/zookeeper");
    // the port at which the clients will connect
    zkProperties.put("clientPort", "2181");
    if (overrideProperties != null) {
      zkProperties.putAll(overrideProperties);
    }
    //disable the per-ip limit on the number of connections since this is a non-production config
    zkProperties.put("maxClientCnxns", "10");
  }
  
  public void setMaxClientCnxns(int num) {
    zkProperties.put("maxClientCnxns", Integer.toString(num));
  }
  
  public void addEnsemble(int serverId, String leaderConnects) {
    zkProperties.put("server." + serverId, leaderConnects);
  }
  
  public void clean() throws Exception {
    FileUtil.removeIfExist(zkProperties.getProperty("dataDir"), false);
  }
  
  public void start() throws Exception {
    if (launcher != null) {
      throw new IllegalStateException("ZookeeperLaucher should be null");
    }
    System.out.println("zookeeper config zkProperties: \n" + JSONSerializer.INSTANCE.toString(zkProperties));

    zkThread = new Thread() {
      public void run() {
        try {
          launcher = create(zkProperties);
          launcher.start();
        } catch (Exception ex) {
          launcher = null;
          System.err.println("Cannot lauch the EmbededZKServer" + ex);
          throw new RuntimeException("Cannot lauch the EmbededZKServer", ex);
        }
      }
    };
    zkThread.start();
    // wait to make sure the server is launched
    Thread.sleep(3000);
  }

  ZookeeperLaucher create(Properties zkProperties) throws ConfigException, IOException {
    QuorumPeerConfig zkConfig = new QuorumPeerConfig();
    zkConfig.parseProperties(zkProperties);
    DatadirCleanupManager purgeMgr =
        new DatadirCleanupManager(zkConfig.getDataDir(), zkConfig.getDataLogDir(),
            zkConfig.getSnapRetainCount(), zkConfig.getPurgeInterval());
    purgeMgr.start();

    if (zkConfig.getServers().size() > 0) {
      return new QuorumPeerMainExt(zkConfig);
    } else {
      System.out
          .println("Either no config or no quorum defined in config, running in standalone mode");
      // there is only server in the quorum -- run as standalone
      return new ZooKeeperServerMainExt(zkConfig);
    }
  }

  @Override
  public String getHost() { return "127.0.0.1"; }

  @Override
  public int getPort() { return this.port; }
  
  @Override
  public String getConnectString() { return getHost() + ":" + getPort() ; }

  @Override
  public void shutdown() {
    if (launcher != null) {
      launcher.shutdown();
      launcher = null;
    }
  }

  static public interface ZookeeperLaucher {
    void start() throws Exception;

    void shutdown();

    QuorumPeerConfig getConfig();
  }

  public class QuorumPeerMainExt extends QuorumPeerMain implements ZookeeperLaucher {
    private QuorumPeerConfig zkConfig;

    public QuorumPeerMainExt(QuorumPeerConfig zkConfig) {
      this.zkConfig = zkConfig;
    }

    public void start() throws Exception {
      runFromConfig(zkConfig);
    }

    public void shutdown() {
      quorumPeer.shutdown();
    }

    @Override
    public QuorumPeerConfig getConfig() {
      return zkConfig;
    }
  }
  
  public class ZooKeeperServerMainExt extends ZooKeeperServerMain implements ZookeeperLaucher {
    private QuorumPeerConfig qConfig;

    public ZooKeeperServerMainExt(QuorumPeerConfig qConfig) {
      this.qConfig = qConfig;
    }

    public void start() throws Exception {
      ServerConfig config = new ServerConfig();
      config.readFrom(qConfig);
      // ManagedUtil.registerLog4jMBeans();
      runFromConfig(config);
    }

    public void shutdown() {
      super.shutdown();
    }

    @Override
    public QuorumPeerConfig getConfig() {
      return qConfig;
    }
  }
}

@@ -1903,17 +1903,16 @@
 nxns%22, %22
-1
 0%22);%0D%0A  

fix default num of connection