/*
 * 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.core.betwixt;

import org.apache.commons.betwixt.AttributeDescriptor;
import org.apache.commons.betwixt.ElementDescriptor;
import org.apache.commons.betwixt.strategy.ValueSuppressionStrategy;

import org.maskat.core.MaskatElement;
import org.maskat.core.event.Bind;
import org.maskat.core.event.Source;
import org.maskat.core.event.Target;

public class MessageSchemaValueSuppressionStrategy extends ValueSuppressionStrategy {
	
	/**the value of the static var FLAG_XXX_XXX is get from xsdConfig.betwixt*/
	private static final String FLAG_SOURCE = "flagSource";
	
	private static final String FLAG_SOURCE_NODE_CHILDNODE_NOT_NULL = "flagSourceNodeAndChildNodeAreNotNull";
	
	private static final String FLAG_SOURCE_NODE_NOT_NULL_CHILDNODE_NULL = "flagSourceNodeIsNotNullChildNodeIsNull";
	
	private static final String FLAG_SOURCE_NODE_NULL_CHILDNODE_NOT_NULL = "flagSourceNodeIsNullChildNodeIsNotNull";
	
	private static final String FLAG_TARGET = "flagTarget";
	
	private static final String FLAG_TARGET_IN_INKEY_NOT_NULL = "flagTargetInIsNotNullInkeyIsNotNull";
	
	private static final String FLAG_TARGET_IN_NULL_INKEY_NOT_NULL = "flagTargetInIsNullInkeyIsNotNull";

	private MaskatElement maskatElement = null;
	
	private ElementDescriptor currentElement = null;
	
	public MessageSchemaValueSuppressionStrategy() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.commons.betwixt.strategy.ValueSuppressionStrategy#suppressAttribute(org.apache.commons.betwixt.AttributeDescriptor,
	 *      java.lang.String)
	 */
	public boolean suppressAttribute(AttributeDescriptor attributeDescriptor, String value) {
		String attributeName = attributeDescriptor.getQualifiedName();
		String propertyName =  attributeDescriptor.getPropertyName();
		
		if (isFlagAttribute(attributeName)) {
			return true;
		}
		
		if (maskatElement != null) {
			if (maskatElement instanceof Source && usedFor(currentElement, FLAG_SOURCE)) {
				if ("name".equals(attributeName) && "node".equals(propertyName)) {
					return ((Source) maskatElement).getNode() == null
						&& ((Source) maskatElement).getChildNode() != null;
				}
				if ("type".equals(attributeName) && "xsd:string".equals(value)) {
					return (((Source) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Source) maskatElement).getTypedChildren(Bind.class).hasNext());
				}
				if ("name".equals(attributeName) && "childNode".equals(propertyName)) {
					return ((Source) maskatElement).getNode() != null
						|| !(((Source) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Source) maskatElement).getTypedChildren(Bind.class).hasNext());
				}
				if ("maxOccurs".equals(attributeName)) {
					return !((((Source) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Source) maskatElement).getTypedChildren(Bind.class).hasNext())
							&& ((Source) maskatElement).getChildNode() != null
							&& ((Source) maskatElement).getNode() == null);
				}
			}
			
			if (maskatElement instanceof Target && usedFor(currentElement, FLAG_TARGET)) {
				if ("name".equals(attributeName) && "in".equals(propertyName)) {
					return ((Target) maskatElement).getIn() == null
						&& ((Target) maskatElement).getInkey() != null;
				}
				if ("type".equals(attributeName) && "xsd:string".equals(value)) {
					return (((Target) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Target) maskatElement).getTypedChildren(Bind.class).hasNext());
				}
				if ("name".equals(attributeName) && "inkey".equals(propertyName)) {
					return ((Target) maskatElement).getIn() != null
						|| !(((Target) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Target) maskatElement).getTypedChildren(Bind.class).hasNext());
				}
				if ("maxOccurs".equals(attributeName)) {
					return !((((Target) maskatElement).getTypedChildren(Bind.class) != null
							&& ((Target) maskatElement).getTypedChildren(Bind.class).hasNext())
							&& ((Target) maskatElement).getInkey() != null
							&& ((Target) maskatElement).getIn() == null);
				}
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.commons.betwixt.strategy.ValueSuppressionStrategy#suppressElement(org.apache.commons.betwixt.ElementDescriptor,
	 *      java.lang.String, java.lang.String, java.lang.String,
	 *      java.lang.Object)
	 */
	public boolean suppressElement(ElementDescriptor element, String namespaceUri,
			String localName, String qualifiedName, Object value) {
		if (value instanceof MaskatElement) {
			maskatElement = (MaskatElement)value;
		}
		currentElement = element;
		
		if (value instanceof Source) {

			if (((Source) value).getTypedChildren(Bind.class) == null 
				||!((Source) value).getTypedChildren(Bind.class).hasNext()) {
				return usedFor(element, FLAG_SOURCE_NODE_CHILDNODE_NOT_NULL)
						|| usedFor(element, FLAG_SOURCE_NODE_NOT_NULL_CHILDNODE_NULL)
						|| usedFor(element, FLAG_SOURCE_NODE_NULL_CHILDNODE_NOT_NULL);
			}
			
			if (((Source) value).getChildNode() == null && ((Source) value).getNode() != null) {
				return usedFor(element, FLAG_SOURCE_NODE_CHILDNODE_NOT_NULL)
						|| usedFor(element, FLAG_SOURCE_NODE_NULL_CHILDNODE_NOT_NULL);
			}

			if (((Source) value).getChildNode() != null && ((Source) value).getNode() == null) {
				return usedFor(element, FLAG_SOURCE_NODE_CHILDNODE_NOT_NULL)
						|| usedFor(element, FLAG_SOURCE_NODE_NOT_NULL_CHILDNODE_NULL);
			}
			
			if (((Source) value).getChildNode() != null && ((Source) value).getNode() != null) {
				return usedFor(element, FLAG_SOURCE_NODE_NULL_CHILDNODE_NOT_NULL)
						|| usedFor(element, FLAG_SOURCE_NODE_NOT_NULL_CHILDNODE_NULL);
			}

		}
		if (value instanceof Target) {
			if (((Target) value).getTypedChildren(Bind.class) == null 
					||!((Target) value).getTypedChildren(Bind.class).hasNext()) {
				return usedFor(element, FLAG_TARGET_IN_INKEY_NOT_NULL)
					|| usedFor(element, FLAG_TARGET_IN_NULL_INKEY_NOT_NULL);
			}
			
			if (((Target) value).getIn() != null) {
				return usedFor(element, FLAG_TARGET_IN_NULL_INKEY_NOT_NULL);
			}
			
			if (((Target) value).getIn() == null) {
				return usedFor(element, FLAG_TARGET_IN_INKEY_NOT_NULL);
			}
		}
		return false;
	}
	
	private boolean isFlagAttribute(String flagAttribute) {
		return FLAG_SOURCE.equals(flagAttribute)
			|| FLAG_SOURCE_NODE_CHILDNODE_NOT_NULL.equals(flagAttribute)
			|| FLAG_SOURCE_NODE_NOT_NULL_CHILDNODE_NULL.equals(flagAttribute)
			|| FLAG_SOURCE_NODE_NULL_CHILDNODE_NOT_NULL.equals(flagAttribute)
			|| FLAG_TARGET.equals(flagAttribute)
			|| FLAG_TARGET_IN_INKEY_NOT_NULL.equals(flagAttribute)
			|| FLAG_TARGET_IN_NULL_INKEY_NOT_NULL.equals(flagAttribute)
			;
	}
	
	private boolean usedFor(ElementDescriptor element, String flagAttribute) {
		return element.getAttributeDescriptors().length > 0
			&& element.getAttributeDescriptor(flagAttribute) != null;
	}

}
