package com.jeeplus.common.utils.base;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;

import com.jeeplus.common.utils.number.NumberUtil;

/**
 * 关于系统Properties的工具类
 * 
 * 1. 统一的读取系统变量，其中Boolean.readBoolean的风格不统一，Double则不支持，都进行了扩展.
 * 
 * 2. 简单的合并系统变量(-D)，环境变量 和默认值，以系统变量优先，在未引入Commons Config时使用.
 * 
 * 3. Properties 本质上是一个HashTable，每次读写都会加锁，所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
 * 在其所关心的属性变化时进行通知.
 * 
 * @author calvin
 */
public class SystemPropertiesUtil {

	/**
	 * 读取Boolean类型的系统变量，为空时返回null，代表未设置，而不是Boolean.getBoolean()的false.
	 */
	public static Boolean getBoolean(String name) {
		String stringResult = System.getProperty(name);
		return BooleanUtil.toBooleanObject(stringResult);
	}

	/**
	 * 读取Boolean类型的系统变量，为空时返回默认值, 而不是Boolean.getBoolean()的false.
	 */
	public static Boolean getBoolean(String name, Boolean defaultValue) {
		String stringResult = System.getProperty(name);
		return BooleanUtil.toBooleanObject(stringResult, defaultValue);
	}

	/**
	 * 读取String类型的系统变量，为空时返回null.
	 */
	public static String getString(String name) {
		return System.getProperty(name);
	}

	/**
	 * 读取String类型的系统变量，为空时返回默认值
	 */
	public static String getString(String name, String defaultValue) {
		return System.getProperty(name, defaultValue);
	}

	/**
	 * 读取Integer类型的系统变量，为空时返回null.
	 */
	public static Integer getInteger(String name) {
		return Integer.getInteger(name);
	}

	/**
	 * 读取Integer类型的系统变量，为空时返回默认值
	 */
	public static Integer getInteger(String name, Integer defaultValue) {
		return Integer.getInteger(name, defaultValue);
	}

	/**
	 * 读取Long类型的系统变量，为空时返回null.
	 */
	public static Long getLong(String name) {
		return Long.getLong(name);
	}

	/**
	 * 读取Integer类型的系统变量，为空时返回默认值
	 */
	public static Long getLong(String name, Long defaultValue) {
		return Long.getLong(name, defaultValue);
	}

	/**
	 * 读取Double类型的系统变量，为空时返回null.
	 */
	public static Double getDouble(String propertyName) {
		return NumberUtil.toDoubleObject(System.getProperty(propertyName));
	}

	/**
	 * 读取Double类型的系统变量，为空时返回默认值.
	 */
	public static Double getDouble(String propertyName, Double defaultValue) {
		Double propertyValue = NumberUtil.toDoubleObject(System.getProperty(propertyName));
		return propertyValue != null ? propertyValue : defaultValue;
	}

	/////////// 简单的合并系统变量(-D)，环境变量 和默认值，以系统变量优先.///////////////

	/**
	 * 合并系统变量(-D)，环境变量 和默认值，以系统变量优先
	 */
	public static String getString(String propertyName, String envName, String defaultValue) {
		checkEnvName(envName);
		String propertyValue = System.getProperty(propertyName);
		if (propertyValue != null) {
			return propertyValue;
		} else {
			propertyValue = System.getenv(envName);
			return propertyValue != null ? propertyValue : defaultValue;
		}
	}

	/**
	 * 合并系统变量(-D)，环境变量 和默认值，以系统变量优先
	 */
	public static Integer getInteger(String propertyName, String envName, Integer defaultValue) {
		checkEnvName(envName);
		Integer propertyValue = NumberUtil.toIntObject(System.getProperty(propertyName));
		if (propertyValue != null) {
			return propertyValue;
		} else {
			propertyValue = NumberUtil.toIntObject(System.getenv(envName));
			return propertyValue != null ? propertyValue : defaultValue;
		}
	}

	/**
	 * 合并系统变量(-D)，环境变量 和默认值，以系统变量优先
	 */
	public static Long getLong(String propertyName, String envName, Long defaultValue) {
		checkEnvName(envName);
		Long propertyValue = NumberUtil.toLongObject(System.getProperty(propertyName));
		if (propertyValue != null) {
			return propertyValue;
		} else {
			propertyValue = NumberUtil.toLongObject(System.getenv(envName));
			return propertyValue != null ? propertyValue : defaultValue;
		}
	}

	/**
	 * 合并系统变量(-D)，环境变量 和默认值，以系统变量优先
	 */
	public static Double getDouble(String propertyName, String envName, Double defaultValue) {
		checkEnvName(envName);
		Double propertyValue = NumberUtil.toDoubleObject(System.getProperty(propertyName));
		if (propertyValue != null) {
			return propertyValue;
		} else {
			propertyValue = NumberUtil.toDoubleObject(System.getenv(envName));
			return propertyValue != null ? propertyValue : defaultValue;
		}
	}

	/**
	 * 合并系统变量(-D)，环境变量 和默认值，以系统变量优先
	 */
	public static Boolean getBoolean(String propertyName, String envName, Boolean defaultValue) {
		checkEnvName(envName);
		Boolean propertyValue = BooleanUtil.toBooleanObject(System.getProperty(propertyName), null);
		if (propertyValue != null) {
			return propertyValue;
		} else {
			propertyValue = BooleanUtil.toBooleanObject(System.getenv(envName), null);
			return propertyValue != null ? propertyValue : defaultValue;
		}
	}

	/////////// ListenableProperties /////////////
	/**
	 * Properties 本质上是一个HashTable，每次读写都会加锁，所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
	 * 在其所关心的属性变化时进行通知.
	 * 
	 * @see ListenableProperties
	 */
	public static synchronized void registerSystemPropertiesListener(PropertiesListener listener) {
		Properties currentProperties = System.getProperties();

		if (!(currentProperties instanceof ListenableProperties)) {
			ListenableProperties newProperties = new ListenableProperties(currentProperties);
			System.setProperties(newProperties);
			currentProperties = newProperties;
		}

		((ListenableProperties) currentProperties).register(listener);
	}

	/**
	 * 检查环境变量名不能有'.'，在linux下不支持
	 */
	private static void checkEnvName(String envName) {
		if (envName == null || envName.indexOf('.') != -1) {
			throw new IllegalArgumentException("envName " + envName + " has dot which is not valid");
		}
	}

	/**
	 * Properties 本质上是一个HashTable，每次读写都会加锁，所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
	 * 在其所关心的属性变化时进行通知.
	 * 
	 * @see PropertiesListener
	 */
	public static class ListenableProperties extends Properties {

		private static final long serialVersionUID = -8282465702074684324L;

		protected List<PropertiesListener> listeners = new CopyOnWriteArrayList<PropertiesListener>();

		public ListenableProperties(Properties properties) {
			super(properties);
		}

		public void register(PropertiesListener listener) {
			listeners.add(listener);
		}

		@Override
		public synchronized Object setProperty(String key, String value) {
			Object result = put(key, value);
			for (PropertiesListener listener : listeners) {
				if (listener.propertyName.equals(key)) {
					listener.onChange(key, value);
				}
			}
			return result;
		}
	}

	/**
	 * 获取所关心的Properties变更的Listener基类.
	 */
	public abstract static class PropertiesListener {

		protected String propertyName;

		public PropertiesListener(String propertyName) {
			this.propertyName = propertyName;
		}

		public abstract void onChange(String propertyName, String value);
	}
}
