package com.iamberry.zk;

import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.List;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.AbstractApplicationContext;

import com.alibaba.dubbo.config.ProtocolConfig;

/**
 * @company	深圳爱贝源科技有限公司
 * @website www.iamberry.com
 * @author 	献
 * @tel		18271840547
 * @date	2016年9月13日
 * @explain 使用zookeeper实现队列
 */
public class ZookeeperQueue {

	/**
	 * 获取实现了slf4j接口的日志
	 */
	private static Logger logger = LoggerFactory.getLogger(ZookeeperQueue.class);
	
	/**
	 * 目录节点
	 */
	private static String ROOT_PATH = "/iamberry";
	private static String TIME_TASK_PATH = "/timetask";
	private static String NOW_HOST_PATH = null;
	private static String  HOST_NODE_PATH = "/host_info";
	
	/**
	 * 当前机器是否能运行定时任务,true:可以运行，false:不可以运行
	 */
	private static volatile boolean TASK_RUN = false;
	public static boolean getTaskRun() {
		return TASK_RUN;
	}
	
	/**
	 * 处理节点选举问题
	 */
	public static void rootElement() {
		logger.info("监听请求....");
		if (!ZookeeperFactory.isZkRun()) {
			throw new RuntimeException("Zookeeper Not Run!");
		}
		final ZkClient client = ZookeeperFactory.getZkClient();
		if (!client.exists(ROOT_PATH)) {
			// 创建一个持久化节点
			client.create(ROOT_PATH, null, CreateMode.PERSISTENT);
		}
		
		final String task = ROOT_PATH + TIME_TASK_PATH;
		if (!client.exists(task)) {
			// 创建一个持久化节点
			client.create(task, null, CreateMode.PERSISTENT);
		}
		
		// 为当前机器创建临时有序的节点,生成的规则为 : /基本目录/host_序号
		final String hostNowPath = client.create(task + "/host_", "host".getBytes(), CreateMode.EPHEMERAL_SEQUENTIAL);
		logger.info("当前机器的节点:" + hostNowPath);
		// 监听此节点的节点变化
		client.subscribeChildChanges(task, new IZkChildListener() {
			@Override
			public void handleChildChange(String path, List<String> nowChilds) throws Exception {
				// 一旦机器下线，自动恢复
				logger.info("节点变化:" + path);
				hostRun(client, task, hostNowPath);
			}
		});
		// 本次选举
		hostRun(client, task, hostNowPath);
	}
	
	/**
	 * 机器信息问题
	 */
	public static void hostInfo() {
		logger.info("监听请求....");
		if (!ZookeeperFactory.isZkRun()) {
			throw new RuntimeException("Zookeeper Not Run!");
		}
		String hostNode = HOST_NODE_PATH;
		ZkClient client = ZookeeperFactory.getZkClient();
		if (!client.exists(ROOT_PATH)) {
			// 创建一个持久化节点
			client.create(ROOT_PATH, null, CreateMode.PERSISTENT);
		}
		
		String task = ROOT_PATH + hostNode;
		if (!client.exists(task)) {
			// 创建一个持久化节点
			client.create(task, null, CreateMode.PERSISTENT);
		}
		// 为当前机器创建临时有序的节点,生成的规则为 : /基本目录/host_序号
		String hostNowPath = client.create(task + "/hostNode_", "HN".getBytes(), CreateMode.EPHEMERAL_SEQUENTIAL);
		logger.info("当前机器的节点:" + hostNowPath);
		// 保存当前节点信息
		NOW_HOST_PATH = hostNowPath;
	}
	
	/**
	 * 选举,不允许用户手动调用
	 */
	private synchronized static void hostRun(ZkClient client, String task, String hostNowPath) {
		// 如果当前节点，确认在运行，那么进行选举
		if (!TASK_RUN) {
			// 启动队列标识
			List<String> childs = client.getChildren(task);
			String[] sorts = childs.toArray(new String[childs.size()]);
			Arrays.sort(sorts);
			// 获取最新的机器
			String minHost = sorts[0];
			// 最小的机器节点，即，获取到了锁的节点
			Integer ver = new Integer(minHost.split("_")[1]);
			Integer nowVer = new Integer(hostNowPath.split("_")[1]);
			if (ver.intValue() == nowVer.intValue()) {
				// 当前机器可以启动
				TASK_RUN = true;
				logger.info("选举出的节点:" + minHost);
			}
		}
	}
	
	/**
	 * 上传当前机器的信息
	 */
	public synchronized static void uploadHostInfo(String host, String port, int state, String applicationName) {
		if (!ZookeeperFactory.isZkRun()) {
			return;
		}
		logger.info("为" + NOW_HOST_PATH + "保存信息," + "HOST:" + host + ",PORT:" + port + ",STATE:" + state + ",applicationName:" + applicationName);
		HostInfo hostInfo = new HostInfo(host, port, applicationName, state);
		ZookeeperFactory.getZkClient().writeData(NOW_HOST_PATH, hostInfo);
	}
	
	/**
	 * 监听当前节点的数据变化
	 * @param state 需要处理的节点状态事件
	 * 	0 : 不处理
	 *  1 : 关闭中
	 *  2 : 已经关闭
	 */
	public synchronized static void monitorNowNodeData(int state, final AbstractApplicationContext context) {
		if (!ZookeeperFactory.isZkRun()) {
			return;
		}
		if (StringUtils.isEmpty(NOW_HOST_PATH)) {
			return;
		}
		// 关注数据变化的时间(数据删除、数据修改)
		ZookeeperFactory.getZkClient().subscribeDataChanges(NOW_HOST_PATH, new IZkDataListener() {
			@Override
			public void handleDataDeleted(String dataPath) throws Exception {
				logger.info("节点数据被删除:" + dataPath);
			}
			@Override
			public void handleDataChange(String dataPath, Object data) throws Exception {
				logger.info("节点数据被修改:" + dataPath);
				// 处理节点数据修改问题
				HostInfo hostInfo = (HostInfo) data;
				if (hostInfo.getState() == 1 && NOW_HOST_PATH.equals(dataPath)) {
					// 若接受到关闭中的状态，执行关闭操作
					// 关闭Spring, 需要手动关闭Spring，因为Spring 没有调用 registerShutdownHook方法
					context.destroy();
					// 关闭DUBBO，判断是否通过环境变量注册
					if (!"true".equals(System.getProperty("dubbo.shutdown.hook"))) {
						// 如果没有注册 ShutdownHook 线程，那么只能手动关闭,否则可以通过exit()自动关闭会回调。
						ProtocolConfig.destroyAll();
					}
					// 关闭虚拟机，一个Dubbo的Main已经注册了ShutdownHook，那么我们调用exit()，则会触发，如此即可
					Runtime.getRuntime().exit(0);
					
					/*
					 * 
						// 此处支持远程关闭tomcat(优雅关闭)
						SocketChannel channel = SocketChannel.open();
						// 设置阻塞
						channel.configureBlocking(true);
						// 连接时间
						channel.socket().setSoTimeout(1000 * 10);
						try {
							// 连接(hostName需要上传)
							channel.connect(new InetSocketAddress("127.0.0.1", 8005));
						} catch (ConnectException e) {
							// 连接超时，是否不存在？
						}
						// 发送命令(需要上传)
						String endline = "\r\n";
						ByteBuffer buffer = ByteBuffer.allocate(100).put(("SHUTDOWN1" + endline).getBytes());
						buffer.flip();
						channel.write(buffer);
						channel.close();
					 */
				}
			}
		});
	}

	/**
	 * 获取所有的机器节点
	 * @return
	 */
	public static List<String> listHostInfo() {
		if (!ZookeeperFactory.isZkRun()) {
			return null;
		}
		List<String> list = ZookeeperFactory.getZkClient().getChildren(ROOT_PATH + HOST_NODE_PATH);
		return list;
	}
	
	/**
	 * 获取节点信息
	 * @param path 如果为null，返回当前节点的信息
	 * @return null表示不存在此节点，或者当前节点没有数据
	 */
	public static HostInfo readNodeData(String path) {
		if (!ZookeeperFactory.isZkRun()) {
			return null;
		}
		if (path == null) {
			path = NOW_HOST_PATH;
		}
		String task = ROOT_PATH + HOST_NODE_PATH + "/" + path;
		return ZookeeperFactory.getZkClient().readData(task);
	}
	
	public static void writeNodeData(String path, HostInfo hostInfo) {
		if (!ZookeeperFactory.isZkRun()) {
			return;
		}
		String task = ROOT_PATH + HOST_NODE_PATH + "/" + path;
		ZookeeperFactory.getZkClient().writeData(task, hostInfo);
	}
}
