package org.apache.ws.jaxme.generator.sg.impl.ccsg;

import java.util.ArrayList;
import java.util.List;

import org.apache.ws.jaxme.generator.sg.AttributeSG;
import org.apache.ws.jaxme.generator.sg.ComplexTypeSG;
import org.apache.ws.jaxme.generator.sg.GroupSG;
import org.apache.ws.jaxme.generator.sg.ObjectSG;
import org.apache.ws.jaxme.generator.sg.ParticleSG;
import org.apache.ws.jaxme.generator.sg.PropertySG;
import org.apache.ws.jaxme.generator.sg.PropertySGChain;
import org.apache.ws.jaxme.generator.sg.TypeSG;
import org.apache.ws.jaxme.generator.sg.impl.PropertySGChainImpl;
import org.apache.ws.jaxme.generator.sg.impl.PropertySGImpl;
import org.apache.ws.jaxme.js.JavaField;
import org.apache.ws.jaxme.js.JavaMethod;
import org.apache.ws.jaxme.js.JavaQName;
import org.apache.ws.jaxme.js.JavaSource;
import org.xml.sax.SAXException;


/** Implementation of a
 * {@link org.apache.ws.jaxme.generator.sg.impl.ccsg.ParticleVisitor},
 * which generates a bean class.
 */
public class BeanGeneratingVisitor extends ParticleVisitorImpl {
	private final JavaSource js;
	private JavaMethod mixedContentMethod;
	private boolean isMixed;
	private ComplexTypeSG ct;

	/** Creates a new instance generating methods into the
	 * given class.
	 * @param pJs Bean class being generated.
	 */
	public BeanGeneratingVisitor(JavaSource pJs) {
		js = pJs;
	}

	private JavaSource getJavaSource() {
		return js;
	}

	private boolean isInheritedAttribute(AttributeSG pAttr, AttributeSG[] pInheritedAttributes) throws SAXException {
		if (pInheritedAttributes != null) {
			String p = pAttr.getPropertySG().getPropertyName();
			for (int i = 0;  i < pInheritedAttributes.length;  i++) {
				if (pInheritedAttributes[i].getPropertySG().getPropertyName().equals(p)) {
					return true;
				}
			}
		}
		return false;
	}

	private void generateAttributes(ComplexTypeSG pType) throws SAXException {
		JavaSource js = getJavaSource();
		AttributeSG[] myAttributes = pType.getAttributes();
		AttributeSG[] inheritedAttributes = null;
		if (pType.getTypeSG().isExtension()) {
			TypeSG tSG = pType.getTypeSG().getExtendedType();
			if (tSG.isComplex()) {
				inheritedAttributes = tSG.getComplexTypeSG().getAttributes();
			}
		}
		for (int i = 0;  i < myAttributes.length;  i++) {
			AttributeSG attr = myAttributes[i];
			if (!isInheritedAttribute(attr, inheritedAttributes)) {
				attr.getPropertySG().generate(js);
			}
		}
	}

	public void emptyType(ComplexTypeSG pType) throws SAXException {
		generateAttributes(pType);
	}

	public void simpleContent(ComplexTypeSG pType) throws SAXException {
		generateAttributes(pType);
		pType.getSimpleContentSG().getPropertySG().generate(getJavaSource());
	}

	protected JavaMethod getGetMixedContentMethod() {
		if (mixedContentMethod == null) {
			JavaSource js = getJavaSource();
			JavaMethod jm = js.newJavaMethod("getContent", List.class, JavaSource.PUBLIC);
			if (!js.isInterface()) {
				JavaField mixedContentList = getJavaSource().newJavaField("content", List.class, JavaSource.PRIVATE);
				mixedContentList.setFinal(true);
				mixedContentList.addLine("new ", ArrayList.class, "()");
				jm.addLine("return ", mixedContentList, ";");
			}
		}
		return mixedContentMethod;
	}

	public void startComplexContent(ComplexTypeSG pType) throws SAXException {
		pType.getComplexContentSG().getElementParticles(); // Triggers the content verification
		ct = pType;
		generateAttributes(pType);
		if (pType.getComplexContentSG().isMixed()) {
			isMixed = true;
			getGetMixedContentMethod();
		}
	}

	private boolean isInheritedParticle(ParticleSG pParticle) throws SAXException {
		String propertyName = pParticle.getPropertySG().getPropertyName();
		if (ct.getTypeSG().isExtension()) {
			TypeSG extType = ct.getTypeSG().getExtendedType();
			if (extType.isComplex()  &&  !extType.getComplexTypeSG().hasSimpleContent()) {
				ParticleSG[] inheritedParticles = extType.getComplexTypeSG().getComplexContentSG().getElementParticles();
				for (int i = 0;  i < inheritedParticles.length;  i++) {
					if (inheritedParticles[i].getPropertySG().getPropertyName().equals(propertyName)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	private void elementParticle(GroupSG pGroupSG, ParticleSG pParticle) throws SAXException {
		if (isInheritedParticle(pParticle)) {
			return;
		}
		final JavaSource pJs = this.js;
		final PropertySG elementSG = pParticle.getPropertySG();
		if (isMixed) {
			if (!pParticle.getObjectSG().getTypeSG().isComplex()) {
				JavaQName qName = GroupUtil.getContentClass(pGroupSG, pParticle, pJs.getQName());
				JavaSource js;
				if (qName.isInnerClass()) {
					js = pJs.newJavaInnerClass(qName.getInnerClassName(), JavaSource.PUBLIC);
					if (!pJs.isInterface()) {
						js.setStatic(true);
					}
				} else {
					js = pJs.getFactory().newJavaSource(qName, JavaSource.PUBLIC);
				}
				if (pJs.isInterface()) {
					js.setType(JavaSource.INTERFACE);
				} else {
					js.addImplements(GroupUtil.getContentClass(pGroupSG, pParticle, ct.getClassContext().getXMLInterfaceName()));
				}
				PropertySGChain chain = ((PropertySGImpl) elementSG).getHeadOfChain();
				PropertySGChain head = new PropertySGChainImpl(chain){
					public String getXMLFieldName(PropertySG pController) throws SAXException {
						return "_value";
					}
					public String getPropertyName(PropertySG pController) throws SAXException {
						return "value";
					}
				};
				PropertySGImpl pSG = new PropertySGImpl(head);
				pSG.generate(js);
			}
		} else {
			elementSG.generate(pJs);
		}

		ObjectSG oSG = pParticle.getObjectSG();
		TypeSG typeSG = oSG.getTypeSG();
		if (!typeSG.isGlobalType()  &&  !typeSG.isGlobalClass()  &&  typeSG.isComplex()) {
			ComplexTypeSG complexTypeSG = typeSG.getComplexTypeSG();
			if (pJs.isInterface()) {
				complexTypeSG.getXMLInterface(pJs);
			} else {
				complexTypeSG.getXMLImplementation(pJs);
			}
        }
	}

	public void simpleElementParticle(GroupSG pGroup, ParticleSG pParticle) throws SAXException {
		elementParticle(pGroup, pParticle);
	}

	public void complexElementParticle(GroupSG pGroup, ParticleSG pParticle) throws SAXException {
		elementParticle(pGroup, pParticle);
	}

	public void wildcardParticle(ParticleSG particle) {
		throw new IllegalStateException("TODO: Add support for wildcards");
	}
}
