package net.databinder.models.ao;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.wicket.util.string.Strings;

import net.databinder.ao.Databinder;
import net.databinder.models.BindingModel;
import net.databinder.models.LoadableWritableModel;
import net.java.ao.Common;
import net.java.ao.RawEntity;
import net.java.ao.schema.FieldNameConverter;

public class EntityModel<T extends RawEntity<K>, K extends Serializable> 
		extends LoadableWritableModel<T> implements BindingModel<T> {
	private K id;
	private Class<T> entityType;
	private Map<String, Object> propertyStore;
	private Object managerKey;
	
	public EntityModel(Class<T> entityType, K id) {
		this(entityType);
		this.id = id;
	}
	
	public EntityModel(Class<T> entityType) {
		this.entityType = entityType;
	}
	
	public EntityModel(T entity) {
		setObject(entity);
	}
	
	public boolean isBound() {
		return id != null;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected T load() {
		if (isBound())
			return Databinder.getEntityManager(managerKey).get(entityType, id);
		return (T) getPropertyStore(); // umm...
	}
	
	@SuppressWarnings("unchecked")
	public void setObject(T entity) {
		unbind();
		entityType = (Class<T>) entity.getEntityType();
		id = Common.getPrimaryKeyValue(entity);
		setTempModelObject(entity);
	}
	
	protected void putDefaultProperties(Map<String, Object> propertyStore) { }
	
	public void unbind() {
		id = null;
		propertyStore = null; 
		detach();
	}

	/**
	 * @return map of  properties to values for Wicket property models
	 */
	public Map<String, Object> getPropertyStore() {
		if (propertyStore == null) {
			propertyStore = new HashMap<String, Object>();
			putDefaultProperties(propertyStore);
		}
		return propertyStore;
	}

	/**
	 * @return map of database fields to their values for creating new entities
	 */
	public Map<String, Object> getFieldMap() {
		Map<String, Object> properties = getPropertyStore(), fields = new HashMap<String, Object>();
		FieldNameConverter conv = Databinder.getEntityManager(managerKey).getFieldNameConverter();
		for (Entry<String, Object> e : properties.entrySet()) {
			String field = e.getKey(), prop = Strings.capitalize(field);
			for (Method m : entityType.getMethods()) {
				// match getter or setter
				if (m.getName().substring(3).equals(prop)) {
					field = conv.getName(m);
					break;
				}
			}
			if (e.getValue() != null)
				fields.put(field, e.getValue());
		}
		return fields;
	}

	public Class<T> getEntityType() {
		return entityType;
	}

	public Object getManagerKey() {
		return managerKey;
	}

	public void setManagerKey(Object managerKey) {
		this.managerKey = managerKey;
	}
	
}

@@ -556,17 +556,22 @@
 leModel%3C
-T
+Object
 %3E implem
@@ -588,17 +588,22 @@
 ngModel%3C
-T
+Object
 %3E %7B%0A%09pri
@@ -1053,17 +1053,22 @@
 otected 
-T
+Object
  load() 
@@ -1169,12 +1169,8 @@
 turn
- (T)
  get
@@ -1189,18 +1189,8 @@
 e();
- // umm...
 %0A%09%7D%0A
@@ -1246,17 +1246,22 @@
 tObject(
-T
+Object
  entity)
@@ -1301,22 +1301,27 @@
 ass%3CT%3E) 
+((T)
 entity
+)
 .getEnti
@@ -1363,16 +1363,19 @@
 eyValue(
+(T)
 entity);

make AO EntityModel untyped :(