package org.arefgard.icerya.container.parse;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.arefgard.icerya.container.BeanDefinitionParseException;
import org.arefgard.icerya.container.ContainerException;
import org.arefgard.icerya.container.IceryaContainer;
import org.arefgard.icerya.container.def.BeanDef;
import org.arefgard.icerya.container.def.BeanResourceDef;
import org.arefgard.icerya.container.def.ConstructorArgDef;
import org.arefgard.icerya.container.def.PropertyDef;
import org.arefgard.icerya.container.def.ReferenceResourceDef;
import org.arefgard.icerya.container.def.ValueResourceDef;
import org.arefgard.icerya.container.injection.impl.EJBInjectionStrategy;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * XML文書で定義されたBean定義ファイルをパースする。
 * 
 * @author Takashi Yamashina
 * @since 1.0.0
 */
public class XmlBeanDefinitionReader {
	
	/** パースした情報を格納するDIコンテナ */
	private IceryaContainer container;
	
	/**
	 * コンストラクタ。
	 * 
	 * @param container パースした情報を格納するDIコンテナ
	 */
	public XmlBeanDefinitionReader(IceryaContainer container) {
		this.container = container;
	}
	
	/**
	 * Bean定義ファイルをパースする。
	 * 
	 * @param path Bean定義ファイルのパス
	 * @throws ContainerException Bean定義ファイルのパースに失敗した場合
	 */
	public void parse(String path) throws ContainerException {
		
		InputStream is = null;
		ByteArrayOutputStream os = null;
		String head = null;
		try {
			is = ClassLoader.getSystemResourceAsStream(path);
			if(is == null) {
				path.getClass().getClassLoader().getResourceAsStream(path);
			}
			os = new ByteArrayOutputStream();
			int len = 0;
			byte[] buf = new byte[4096];
			len = is.read(buf);
			os.write(buf, 0, len);
			head = os.toString();
		}catch(Exception e) {
			throw new BeanDefinitionParseException(e);
		}finally {
			if(is != null) {
				try {
					is.close();
				}catch(Exception e) {
					// ignore
				}
			}
			if(os != null) {
				try {
					os.close();
				}catch(Exception e) {
					// ignore
				}
			}
		}
		// 先頭文字列を解析する
		if(head == null) {
			throw new BeanDefinitionParseException();
		}else if(head.indexOf("<container") > 0) {
			// IceryaContainer
		}else {
			throw new BeanDefinitionParseException();
		}
		
		
		// SAXパーサの取得
		XMLReader reader = null;
		try {
			reader = XMLReaderFactory.createXMLReader();
		} catch (SAXException e) {
			throw new BeanDefinitionParseException("Creation XMLReader failed.", e);
		}
		
		// ハンドラの設定
		reader.setContentHandler(new BeanHandler(container));
		is = ClassLoader.getSystemResourceAsStream(path);
		if(is == null) {
			is = this.getClass().getClassLoader().getResourceAsStream(path);
		}

		try {
			reader.parse(new InputSource(is));
		} catch (FileNotFoundException e) {
			throw new BeanDefinitionParseException("Bean Definition File Parsing failed.", e);
		} catch (IOException e) {
			throw new BeanDefinitionParseException("Bean Definition File Parsing failed.", e);
		} catch (SAXException e) {
			throw new BeanDefinitionParseException("Bean Definition File Parsing failed.", e);
		}finally {
			try {
				if(is != null) {
					is.close();
				}
			}catch(Exception ignored) {}
		}
		
		// ResourceDefの解決
		Map<String, BeanDef> beanMap = container.getAllBeanDef();
		Set<String> keys1 = beanMap.keySet();
		for(Iterator<String> itr1 = keys1.iterator(); itr1.hasNext();) {
			String key1 = itr1.next();
			BeanDef beanDef = beanMap.get(key1);
			
			// EJBInjectionチェック
//			if(EJBInjectionStrategy.checkEJB(beanDef.getBeanClass())) {
//				beanDef.setInjectionStrategy(new EJBInjectionStrategy());
//			}
			
			// Propertyの解決
			Map<String, PropertyDef> properties = beanDef.getProperties();
			Set<String> keys2 = properties.keySet();
			for(Iterator<String> itr2 = keys2.iterator(); itr2.hasNext();) {
				String key2 = itr2.next();
				PropertyDef propertyDef = beanDef.getProperty(key2);
				if(propertyDef.getResourceDef() instanceof ReferenceResourceDef) {
					continue;
				}else if(propertyDef.getResourceDef() instanceof BeanResourceDef) {
					continue;
				}else {
					// ValueResourceDefとして正しい型で保存する
//					ValueResourceDef resourceDef = (ValueResourceDef)propertyDef.getResourceDef();
//					String type = null;
//					type = getType(key2, beanDef, propertyDef);
//					
//					String value = (String)resourceDef.getValue();
//					if(type.equals("byte") || type.equals("java.lang.Byte")) {
//						resourceDef.setValue(new Byte(value));
//					}else if(type.equals("short") || type.equals("java.lang.Short")) {
//						resourceDef.setValue(new Short(value));
//					}else if(type.equals("int") || type.equals("java.lang.Integer")) {
//						resourceDef.setValue(new Integer(value));
//					}else if(type.equals("long") || type.equals("java.lang.Long")) {
//						resourceDef.setValue(new Long(value));
//					}else if(type.equals("float") || type.equals("java.lang.Float")) {
//						resourceDef.setValue(new Float(value));
//					}else if(type.equals("double") || type.equals("java.lang.Double")) {
//						resourceDef.setValue(new Double(value));
//					}else if(type.equals("char") || type.equals("java.lang.Character")) {
//						resourceDef.setValue(new Character(value.toCharArray()[0]));
//					}
				}
			}
			
			// ConstructorArgの解決
			List<ConstructorArgDef> constructorArgs = beanDef.getConstructorArgs();
			int length = constructorArgs.size();
			for(int i = 0; i < length; i++) {
				ConstructorArgDef constructorArgDef = constructorArgs.get(i);
				ValueResourceDef valueResourceDef = (ValueResourceDef)constructorArgDef.getResourceDef();
				if(beanMap.containsKey(valueResourceDef.getValue())) {
					// 参照している場合は、ReferenceResourceDefに置き換える
					ReferenceResourceDef refDef = new ReferenceResourceDef((String)((ValueResourceDef)constructorArgDef.getResourceDef()).getValue());
					constructorArgDef.setResourceDef(refDef);
					constructorArgDef.setType(container.getBean(refDef.getRef()).getClass());
				}
			}
		}
		
		// AOP実行
		container.weaving();
		
		
	}

//	private String getType(String target, BeanDef beanDef, PropertyDef propertyDef) {
//		Class cls = beanDef.getBeanClass();
//		Field[] fields = cls.getDeclaredFields();
//		int len = fields.length;
//		for(int i = 0; i < len; i++) {
//			if(fields[i].getName().equals(target)) {
//				propertyDef.setType(fields[i].getType());
//				return fields[i].getType().getName();
//			}
//		}
//		// 親クラス
//		cls = cls.getSuperclass();
//		while(!cls.equals(Object.class)) {
//			fields = cls.getDeclaredFields();
//			len = fields.length;
//			for(int i = 0; i < len; i++) {
//				if(fields[i].getName().equals(target)) {
//					propertyDef.setType(fields[i].getType());
//					return fields[i].getType().getName();
//				}
//			}
//			cls = cls.getSuperclass();
//		}
//		return null;
//	}
	
	
}
