/******************************************************************************* 
 * Copyright (c) 2009 Red Hat, Inc. 
 * Distributed under license by Red Hat, Inc. All rights reserved. 
 * This program is 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: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/ 
package org.jboss.tools.cdi.internal.core.impl;

import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaModelException;
import org.jboss.tools.cdi.core.CDICorePlugin;
import org.jboss.tools.cdi.core.IBeanMember;
import org.jboss.tools.cdi.core.IClassBean;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.BeanMemberDefinition;
import org.jboss.tools.common.java.IParametedType;
import org.jboss.tools.common.java.ParametedType;
import org.jboss.tools.common.java.ParametedTypeFactory;
import org.jboss.tools.common.java.TypeDeclaration;

/**
 * 
 * @author Viacheslav Kabanovich
 *
 */
public abstract class BeanMember extends AbstractBeanElement implements IBeanMember {
	protected IClassBean classBean;
	protected TypeDeclaration typeDeclaration;

	public BeanMember() {}

	@Override
	public BeanMemberDefinition getDefinition() {
		return (BeanMemberDefinition)definition;
	}

	protected void setMember(IJavaElement member) {
		typeDeclaration = getTypeDeclaration(getDefinition(), getCDIProject().getNature().getTypeFactory());
	}

	@Override
	public IJavaElement getSourceElement() {
		return (IJavaElement)getDefinition().getMember();
	}

	public static TypeDeclaration getTypeDeclaration(AbstractMemberDefinition definition, ParametedTypeFactory typeFactory) {
		IJavaElement member = (IJavaElement)definition.getMember();
		try {
			String returnType = null;
			IMember currentMember = null;
			if (member instanceof IField) {
				returnType = ((IField)member).getTypeSignature();
				currentMember = (IMember)member;
			} else if (member instanceof IMethod) {
				returnType = ((IMethod)member).getReturnType();
				currentMember = (IMember)member;
			} else if (member instanceof ILocalVariable) {
				returnType = ((ILocalVariable)member).getTypeSignature();
				currentMember = ((ILocalVariable)member).getDeclaringMember();
			}
			if(returnType != null) {
				ParametedType p = typeFactory.getParametedType(currentMember, returnType);
				if(p != null) {

					int offset = -1;
					int length = 0;
					String content = definition.getTypeDefinition().getContent();
					if(content != null) {
						ISourceRange sr = ((ISourceReference)member).getSourceRange();
						ISourceRange nr = ((ISourceReference)member).getNameRange();
						if(sr != null && nr != null && sr.getOffset() < nr.getOffset() && nr.getOffset() < content.length()) {
							String start = content.substring(sr.getOffset(), nr.getOffset());
							int off = -1;
							int off0 = -1;
							int bc = 0;
							for (int i = start.length() - 1; i >= 0; i--) {
								char ch = start.charAt(i);
								if(ch == '>') bc++; else if(ch == '<') bc--;
								if(Character.isWhitespace(ch)) {
									if(off >= 0 && bc <= 0) break;
								} else if(Character.isJavaIdentifierPart(ch) || ch == '.' || ch == '$' || ch == '<' || ch == '>') {
									off = i;
									if(off0 < 0) off0 = i + 1;
								}
							}
							if(off >= 0) {
								offset = sr.getOffset() + off;
								length = off0 - off;
							}
						}
					}

					return new TypeDeclaration(p, member.getResource(), offset, length);
				}
			}
		} catch (JavaModelException e) {
			CDICorePlugin.getDefault().logError(e);
		}
		return null;
	}

	/**
	 * These method is used to construct fake members that get all 
	 * annotations from this member, but should have another type;
	 * e.g. in implementation of Seam 3 persistence extension.
	 * @param d
	 */
	public void setTypeDeclaration(TypeDeclaration typeDeclaration) {
		this.typeDeclaration = typeDeclaration;
	}

	public TypeDeclaration getTypeDeclaration() {
		return typeDeclaration;
	}

	public IClassBean getClassBean() {
		return classBean;
	}

	public void setClassBean(ClassBean classBean) {
		this.classBean = classBean;
		setParent(classBean);
	}

	public IParametedType getType() {
		return typeDeclaration;
	}

	public IParametedType getMemberType() {
		return typeDeclaration;
	}

	public IResource getResource() {
		if(definition.getOriginalDefinition() != null) {
			return definition.getOriginalDefinition().getResource();
		}
		return super.getResource();
	}

	protected ISourceReference getSourceReference() {
		return getSourceMember();
	}

	protected ISourceRange getSourceRange() {
		ISourceRange result = null;
		try {
			result = getSourceReference().getSourceRange();
		} catch (JavaModelException e) {
			CDICorePlugin.getDefault().logError(e);
		}
		return result;
	}

	public int getLength() {
		if(definition.getOriginalDefinition() != null) {
			return definition.getOriginalDefinition().getLength();
		}
		ISourceRange r = getSourceRange();
		return r == null ? 0 : r.getLength();
	}

	public int getStartPosition() {
		if(definition.getOriginalDefinition() != null) {
			return definition.getOriginalDefinition().getStartPosition();
		}
		ISourceRange r = getSourceRange();
		return r == null ? 0 : r.getOffset();
	}

	public boolean isNullable() {
		return typeDeclaration == null ? false : !typeDeclaration.isPrimitive();
	}
}