package com.wecloud.netty.dispatch.factory;

import com.wecloud.netty.dispatch.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author lixiaozhong
 */
@Slf4j
public abstract class AbstractFactory {

	private Map<Class<?>, Object> objectMap = new ConcurrentHashMap<>();
	private Map<Class<?>, Boolean> canNotInstanceMap = new ConcurrentHashMap<>();
	private ReentrantLock lock = new ReentrantLock();

	public <T> void put(Class<T> defineClass, T object) {
		objectMap.put(defineClass, object);
	}

	/**
	 * 根据class获取对象，均为单列(有待验证😂)
	 *
	 * @param classType
	 * @return T
	 */
	public <T> T getObject(Class<T> classType) {
		return getObject(classType, false, false);
	}

	/**
	 *
	 * @param classType
	 * @param createNew
	 * @return T
	 */
	public <T> T getObject(Class<T> classType, boolean createNew) {
		return getObject(classType, createNew, false);
	}

	/**
	 *
	 * @param classType ：被实例化的class对象
	 * @param createNew ：true：不管对象是否存在，都新创建对象；false：如果对象已经存在就不新建对象
	 * @param cover     ：true：如果新建了对象，则覆盖原来的对象。false：不执行覆盖操作
	 * @return T
	 */
	@SuppressWarnings("unchecked")
	public <T> T getObject(Class<T> classType, boolean createNew, boolean cover) {
		Object object = null;
		if (null != classType) {
			object = objectMap.get(classType);
			if (createNew) {
				object = createObject(classType);
				if ((cover) && null != object) {
					objectMap.put(classType, object);
				}
			} else if (null == object) {
				final ReentrantLock lock = this.lock;
				lock.lock();
				try {
					boolean has = objectMap.containsKey(classType);
					object = objectMap.get(classType);
					if (!has) {
						object = createObject(classType);
						// 原来对象不存在，或者覆盖，并且新创建的对象不能为空
						if ((cover) && null != object) {
							objectMap.put(classType, object);
						}
					}
				} finally {
					lock.unlock();
				}
			}
		}
		return (T) object;
	}

	/**
	 * 根据class反射创建对象
	 *
	 * @param clazz
	 * @return
	 */
	public Object createObject(Class<?> clazz) {
		Object object = null;
		if (!canNotInstanceMap.containsKey(clazz)) {
			try {
				if (ClassUtil.isCanInstance(clazz)) {
					object = clazz.newInstance();
				} else if (clazz.isArray()) {
					Class<?> componentType = clazz.getComponentType();
					object = Array.newInstance(componentType, 0);
				}
			} catch (Exception e) {
				canNotInstanceMap.put(clazz, false);
				log.error("初始化对象失败！", e);
			}
		}
		return object;
	}
}
