/*******************************************************************************
 * Copyright (c) 2004, 2010 IBM Corporation and others.
 * 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:
 *     Andrew Niefer (IBM Corporation) - initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;

import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;

/**
 * Binding for a field.
 */
public class CPPField extends CPPVariable implements ICPPField {
    public static class CPPFieldProblem extends ProblemBinding implements ICPPField {
        private ICPPClassType fOwner;

		public CPPFieldProblem(ICPPClassType owner, IASTNode node, int id, char[] arg) {
            super(node, id, arg);
            fOwner= owner;
        }

        @Override
		public int getVisibility() {
            return v_private;
        }

        @Override
		public ICPPClassType getClassOwner() {
            return fOwner;
        }

		@Override
		public ICompositeType getCompositeTypeOwner() {
			return getClassOwner();
		}
    }
    
	public CPPField(IASTName name) {
		super(name);
	}

	public IASTDeclaration getPrimaryDeclaration() {
		// First check if we already know it
		IASTDeclaration decl= findDeclaration(getDefinition());
		if (decl != null) {
			return decl;
		}
		
	    IASTName[] declarations = (IASTName[]) getDeclarations();
		if (declarations != null) {
			for (IASTName name : declarations) {
				decl= findDeclaration(name);
				if (decl != null) {
					return decl;
				}
			}
		}
		
		char[] myName = getNameCharArray();
		
		ICPPClassScope scope = (ICPPClassScope) getScope();
		ICPPASTCompositeTypeSpecifier compSpec = (ICPPASTCompositeTypeSpecifier) ASTInternal.getPhysicalNodeOfScope(scope);
		IASTDeclaration[] members = compSpec.getMembers();
		for (IASTDeclaration member : members) {
			if (member instanceof IASTSimpleDeclaration) {
				IASTDeclarator[] dtors = ((IASTSimpleDeclaration) member).getDeclarators();
				for (IASTDeclarator dtor : dtors) {
					IASTName name = dtor.getName();
					if (CharArrayUtils.equals(name.getLookupKey(), myName) && name.resolveBinding() == this) {
						return member;
					}
				}
			}
		}
		return null;
	}

	private IASTDeclaration findDeclaration(IASTNode node) {
		while (node != null && !(node instanceof IASTDeclaration)) {
			node = node.getParent();
		}
		if (node != null && node.getParent() instanceof ICPPASTCompositeTypeSpecifier) {
			return (IASTDeclaration) node;
    	}
		return null;
	}

	@Override
	public int getVisibility() {
		ICPPASTVisibilityLabel vis = null;
		IASTDeclaration decl = getPrimaryDeclaration();
		if (decl != null) {
			IASTCompositeTypeSpecifier cls = (IASTCompositeTypeSpecifier) decl.getParent();
			IASTDeclaration[] members = cls.getMembers();

			for (IASTDeclaration member : members) {
				if (member instanceof ICPPASTVisibilityLabel) {
					vis = (ICPPASTVisibilityLabel) member;
				} else if (member == decl) {
					break;
				}
			}
		
			if (vis != null) {
				return vis.getVisibility();
			} else if (cls.getKey() == ICPPASTCompositeTypeSpecifier.k_class) {
				return ICPPASTVisibilityLabel.v_private;
			}
		}
		return ICPPASTVisibilityLabel.v_public;
	}
	
	@Override
	public ICPPClassType getClassOwner() {
		ICPPClassScope scope = (ICPPClassScope) getScope();
		return scope.getClassType();
	}
	
    @Override
	public boolean isStatic() {
        // definition of a static field doesn't necessarily say static
		if (getDeclarations() == null) {
			IASTNode def= getDefinition();
			if (def instanceof ICPPASTQualifiedName) {
				return true;
			}
		}
		return super.isStatic();
	}
	
    @Override
	public boolean isMutable() {
        return hasStorageClass(IASTDeclSpecifier.sc_mutable);
    }
    
    @Override
	public boolean isExtern() {
        //7.1.1-5 The extern specifier can not be used in the declaration of class members
        return false;
    }
    
	@Override
	public ICompositeType getCompositeTypeOwner() {
		return getClassOwner();
	}
}
