package org.arefgard.container;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * シングルトンパターンを使ったDIContainer実装。
 * 
 * @author T.A.K.A.Dark
 *
 */
public class SingletonIceryaContainer implements IceryaContainer {

	private Log log = LogFactory.getLog(SingletonIceryaContainer.class);
	
	private ConnectionDef connection = null;
	
	/** ビジネスドメインを保持する内部コンテナ */
	private Map<String, BusinessDef> services = new java.util.HashMap<String, BusinessDef>();
	
	/** ドメインモデルを保持する内部コンテナ */
	private Map<String, DomainDef> domains = new java.util.HashMap<String, DomainDef>();
	
	/** 自身のインスタンス。 */
	private static SingletonIceryaContainer instance = null;
	
	private SingletonIceryaContainer() {}
	
	/**
	 * インスタンスを取得する。
	 * 
	 * @return 自身のインスタンス
	 */
	public static IceryaContainer newInstance() {
		if(instance == null) {
			instance = new SingletonIceryaContainer();
		}
		return instance;
	}
	
	public Object create(String ref) throws ContainerException {
		Reference obj = new Reference(ref);
		return this.create(obj);
	}
	
	public Object create(String ref, Map<String, Object> property) throws ContainerException {
		Reference obj = new Reference(ref);
		return this.create(obj, property);
	}
	
	/** 
	 * オブジェクトを生成する。
	 * 始めにサービスを検索し、次にBeanを検索する。
	 * 
	 * @param name 生成するオブジェクト名
	 * @return 生成したオブジェクト
	 */
	public Object create(Reference ref) throws ContainerException {
		log.debug(ref);
		return this.create(ref, null);
	}
	
	public Object create(Reference ref, Map<String, Object> property) throws ContainerException {
		Object result = null;
		if(this.checkService(ref)) {
			BusinessDef businessDef = getBusinessDef(ref.getNameSpace());
			ServiceDef serviceDef = businessDef.getServiceDef(ref.getTarget());
			try {
				result = serviceDef.create(property);
			}catch(Exception e) {
				log.error("error", e);
			}
		}else {
			DomainDef domainDef = getDomainDef(ref.getNameSpace());
			BeanDef beanDef = domainDef.getBeanDef(ref.getTarget());
			try {
				result = beanDef.create(property);
			}catch(Exception e) {
				log.error("error", e);
			}
		}
		return result;
	}
	
	public Object create(Class clazz) throws ContainerException {
		try {
			return clazz.newInstance();
		} catch(Exception e) {
			log.error("error", e);
			throw new ContainerException("error", e);
		}
	}
	
	public BusinessService createService(String path) throws ContainerException {
		Reference ref = new Reference(path);
		BusinessService result = new BusinessService(this, ref);
		return result;
	}
	
	public Object[] find(String query) throws ContainerException {
		return null;
	}

	public Object load(String key) throws ContainerException {
		return null;
	}

	/**
	 * 
	 */
	public void persist(Object obj) throws ContainerException {
		
	}
	
	/**
	 *  コンポーネント定義を取得する。
	 *  
	 *  @param def コンポーネント定義
	 */
	public synchronized void addBusinessDef(BusinessDef def) {
		services.put(def.getName(), def);
	}
	
	public synchronized BusinessDef getBusinessDef(String key) {
		return services.get(key);
	}

	public synchronized void addDomainDef(DomainDef def) {
		domains.put(def.getName(), def);
	}
	
	public synchronized DomainDef getDomainDef(String key) {
		return domains.get(key);
	}
	
	private boolean checkService(Reference ref) {
		BusinessDef businessDef = getBusinessDef(ref.getNameSpace());
		if(businessDef == null) {
			return false;
		}
		return businessDef.contains(ref.getTarget());
	}
	
	public synchronized void setConnectionDef(ConnectionDef connection) {
		this.connection = connection;
	}
	
	public synchronized ConnectionDef getConnectionDef() {
		return this.connection;
	}
}
