package com.hammurapi.reasoning.impl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.hammurapi.convert.AtomicConverter;
import com.hammurapi.convert.Converter;
import com.hammurapi.reasoning.spi.Superseder;
import com.hammurapi.util.Context;

public class BeanSupersederAtomicConverter implements AtomicConverter<Object, Superseder> {
	private static final Logger logger = Logger.getLogger(BeanSupersederAtomicConverter.class.getName());

	@Override
	public Superseder convert(final Object source, Converter master, Context context, ClassLoader classLoader) {
		return new Superseder() {

			@Override
			public boolean supersedes(Object subFact) {
				if (source==subFact) {
					return false;
				}
				
				if (source==null) {
					return false;
				}
				
				if (subFact==null) {
					return false;
				}
				
				if (subFact.getClass().isInstance(source) && !source.getClass().isInstance(subFact)) {
					try {
						Map<String, Object> sourceData = extractData(source);
						Map<String, Object> subFactData = extractData(subFact);
						if (sourceData.keySet().containsAll(subFactData.keySet())) {
							for (String key: subFactData.keySet()) {
								Object sourceValue = sourceData.get(key);
								Object subFactValue = subFactData.get(key);
								if (sourceValue==null) {
									if (subFactValue!=null) {
										return false;
									}
								} else {
									if (subFactValue==null) {
										return false;
									}
									
									if (!sourceValue.equals(subFactValue)) {
										return false;
									}
								}
							}
							return true;
						}
					} catch (InvocationTargetException e) {
						logger.log(Level.WARNING, "Data extraction failed: "+e, e);
						return false;
					} catch (IllegalAccessException e) {
						logger.log(Level.WARNING, "Data extraction failed: "+e, e);
						return false;
					}
				}
				return false;
			}
			
		};
	}
	
	private static Map<String, Object> extractData(Object obj) throws IllegalAccessException, InvocationTargetException {
		Map<String, Object> ret = new HashMap<String, Object>();
		for (Method method: obj.getClass().getMethods()) {
			if (method.getName().startsWith("get")
					&& !Modifier.isStatic(method.getModifiers())
					&& method.getParameterTypes().length==0 
					&& !Object.class.equals(method.getDeclaringClass())) {
				ret.put("method:"+method.getName(), method.invoke(obj));
			}
		}
		
		for (Field field: obj.getClass().getFields()) {
			if (!Modifier.isStatic(field.getModifiers())) {
				ret.put("field:"+field.getName(), field.get(obj));
			}
		}
		
		return ret;
	}

	@Override
	public Class<Object> getSourceType() {
		return Object.class;
	}

	@Override
	public Class<? extends Superseder> getTargetType() {
		return Superseder.class;
	}

}
