/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Maskat Project - initial API and implementation
 */
package org.maskat.ui.views.properties;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.DynaProperty;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;

import org.maskat.core.layout.LayoutElement;
import org.maskat.core.layout.custom.DynaComponent;
import org.maskat.ui.editors.layout.commands.UndoableSetPropertyCommand;
import org.maskat.ui.editors.layout.tools.ComponentUtils;
import org.maskat.ui.views.properties.descriptor.MaskatTextPropertyDescriptor;

public class DynaBeanPropertySource implements IPropertySource {

	private static Map classToDescriptorsMap = new HashMap();

	private DynaBean dynaBean;
	
	private CommandStack commandStack;
	
	public DynaBeanPropertySource(DynaBean dynaBean) {
		super();
		this.dynaBean = dynaBean;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getEditableValue()
	 */
	public Object getEditableValue() {
		return dynaBean;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
	 */
	public IPropertyDescriptor[] getPropertyDescriptors() {
		DynaClass dynaClass = dynaBean.getDynaClass();
		if (classToDescriptorsMap.containsKey(dynaClass)) {
			return (IPropertyDescriptor[]) classToDescriptorsMap.get(dynaClass);
		}

		DynaProperty[] properties = dynaClass.getDynaProperties();
		int exceptionProps = 0;
		for (int i = 0; i < properties.length; i++) {
			if (cannotSetInPropertySheet(properties[i])) {
				exceptionProps ++;
			}
		}
		//properties.length-exceptionItems: desc,parent,children.
		IPropertyDescriptor[] descriptors = new IPropertyDescriptor[properties.length-exceptionProps]; 
		int idx = 0;
		for (int i = 0; i < properties.length; i++) {
			if (cannotSetInPropertySheet(properties[i])) {
				continue;
			}
			descriptors[idx++] = createPropertyDescriptor(properties[i]);
		}

		classToDescriptorsMap.put(dynaClass, descriptors);
		return descriptors;
	}
	
	private boolean cannotSetInPropertySheet(DynaProperty property) {
		//TODO should use type to judge. 
//		if ("desc".equals(property.getName()) || "parent".equals(property.getName())
//				|| "children".equals(property.getName())) {
//			return true;
//		}
//		return false;
		return !DynaComponent.isAttributeProperty(property.getName());
	}

	private IPropertyDescriptor createPropertyDescriptor(DynaProperty property) {
		String displayName = property.getName();
//		Class propertyType = property.getType();
//		if (Boolean.TYPE.equals(propertyType) || Boolean.class.equals(propertyType)) {
//			return new ComboBoxPropertyDescriptor(property, displayName, new String[] {
//					"false", "true" });
//		}
//		return new TextPropertyDescriptor(property, displayName);
		return new MaskatTextPropertyDescriptor(property, displayName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
	 */
	public Object getPropertyValue(Object id) {
		return dynaBean.get(getPropertyName(id));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#isPropertySet(java.lang.Object)
	 */
	public boolean isPropertySet(Object id) {
//		DynaProperty property = (DynaProperty) id;
//		if (property.getType().isPrimitive()) {
//			return true;
//		}
//		return dynaBean.get(property.getName()) != null;
		return false;
	}

	private String getPropertyName(Object id) {
		return ((DynaProperty) id).getName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#resetPropertyValue(java.lang.Object)
	 */
	public void resetPropertyValue(Object id) {
//		dynaBean.set(getPropertyName(id), null);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object,
	 *      java.lang.Object)
	 */
	public void setPropertyValue(Object id, Object value) {
//		dynaBean.set(getPropertyName(id), value);
		if(value != null && "name".equals(getPropertyName(id)) && ComponentUtils.isExistenceId(((LayoutElement)dynaBean).getLayout(), (String)value)) {
			return;
		} else if (value == null && "name".equals(getPropertyName(id))) {
			return;
		}

		if (commandStack != null) {
			Object oldValue = null;
			try {
				oldValue = BeanUtils.getProperty(dynaBean, getPropertyName(id));
			} catch (Exception e) {
				e.printStackTrace();
			}
			if ((oldValue == null && value != null) || (oldValue != null && !oldValue.equals(value))) {
				commandStack.execute(new UndoableSetPropertyCommand(dynaBean, getPropertyName(id), value));
			}
			return;
		}
		try {
			if (dynaBean.get(getPropertyName(id)) == null && value == null) {
				return;
			}
			BeanUtils.setProperty(dynaBean, getPropertyName(id), value);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	public void setCommandStack(CommandStack commandStack) {
		this.commandStack = commandStack;
	}
}
