/*******************************************************************************
 * Copyright (c) 2004, 2007 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:
 * IBM - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
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.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.parser.util.ArrayUtil;

/**
 * @author jcamelon
 */
public class CPPASTDeclarator extends CPPASTNode implements IASTDeclarator {
    private IASTInitializer initializer;
    private IASTName name;
    private IASTDeclarator nestedDeclarator;
    private IASTPointerOperator [] pointerOps = null;
    private int pointerOpsPos=-1;

   
    public CPPASTDeclarator() {
	}

	public CPPASTDeclarator(IASTName name) {
		setName(name);
	}
    
    public CPPASTDeclarator(IASTName name, IASTInitializer initializer) {
		this(name);
		setInitializer(initializer);
	}

	public IASTPointerOperator[] getPointerOperators() {
        if( pointerOps == null ) return IASTPointerOperator.EMPTY_ARRAY;
        pointerOps = (IASTPointerOperator[]) ArrayUtil.removeNullsAfter( IASTPointerOperator.class, pointerOps, pointerOpsPos );
        return pointerOps;
    }

    public IASTDeclarator getNestedDeclarator() {
        return nestedDeclarator;
    }

    public IASTName getName() {
        return name;
    }

    public IASTInitializer getInitializer() {
        return initializer;
    }

    public void setInitializer(IASTInitializer initializer) {
        this.initializer = initializer;
        if (initializer != null) {
			initializer.setParent(this);
			initializer.setPropertyInParent(INITIALIZER);
		}
    }

    public void addPointerOperator(IASTPointerOperator operator) {
    	if (operator != null) {
    		operator.setParent(this);
			operator.setPropertyInParent(POINTER_OPERATOR);
    		pointerOps = (IASTPointerOperator[]) ArrayUtil.append( IASTPointerOperator.class, pointerOps, ++pointerOpsPos, operator );
    	}
    }

    public void setNestedDeclarator(IASTDeclarator nested) {
        this.nestedDeclarator = nested;
        if (nested != null) {
			nested.setParent(this);
			nested.setPropertyInParent(NESTED_DECLARATOR);
		}
    }


    public void setName(IASTName name) {
        this.name = name;
        if (name != null) {
			name.setParent(this);
			name.setPropertyInParent(DECLARATOR_NAME);
		}
    }

    public boolean accept( ASTVisitor action ){
        if( action.shouldVisitDeclarators ){
		    switch( action.visit( this ) ){
	            case ASTVisitor.PROCESS_ABORT : return false;
	            case ASTVisitor.PROCESS_SKIP  : return true;
	            default : break;
	        }
		}
        
        IASTPointerOperator [] ptrOps = getPointerOperators();
        for ( int i = 0; i < ptrOps.length; i++ ) {
            if( !ptrOps[i].accept( action ) ) return false;
        }
        
        if( getPropertyInParent() != IASTTypeId.ABSTRACT_DECLARATOR &&
    		nestedDeclarator == null )
		{
           if( getParent() instanceof IASTDeclarator )
            {
                IASTDeclarator outermostDeclarator = (IASTDeclarator) getParent();
                while( outermostDeclarator.getParent() instanceof IASTDeclarator )
                    outermostDeclarator = (IASTDeclarator) outermostDeclarator.getParent();
                if( outermostDeclarator.getPropertyInParent() != IASTTypeId.ABSTRACT_DECLARATOR )
                    if( name != null ) if( !name.accept( action ) ) return false;
            }
            else
                if( name != null ) if( !name.accept( action ) ) return false;
		}
        
        if( nestedDeclarator != null ) if( !nestedDeclarator.accept( action ) ) return false;
      
        if( action.shouldVisitDeclarators ){
		    switch( action.leave( this ) ){
	            case ASTVisitor.PROCESS_ABORT : return false;
	            case ASTVisitor.PROCESS_SKIP  : return true;
	            default : break;
	        }
		}
          
        return postAccept( action );
    }
    
    protected boolean postAccept( ASTVisitor action ){
        if( initializer != null ) if( !initializer.accept( action ) ) return false;
        return true;
    }


	public int getRoleForName(IASTName n) {
        IASTNode getParent = getParent();
        boolean fnDtor = ( this instanceof IASTFunctionDeclarator );
        if( getParent instanceof IASTDeclaration )
        {
            if( getParent instanceof IASTFunctionDefinition )
                return r_definition;
            if( getParent instanceof IASTSimpleDeclaration )
            {
                if( getInitializer() != null )
                    return r_definition;
                
                IASTSimpleDeclaration sd = (IASTSimpleDeclaration) getParent;
                int storage = sd.getDeclSpecifier().getStorageClass(); 
                if( storage == IASTDeclSpecifier.sc_extern || 
                    storage == IASTDeclSpecifier.sc_typedef ||
                    storage == IASTDeclSpecifier.sc_static )
                {
                    return r_declaration;
                }
            }
            return fnDtor ? r_declaration : r_definition;
        }
        if( getParent instanceof IASTTypeId )
            return r_reference;
        if( getParent instanceof IASTDeclarator )
        {
            IASTNode t = getParent;
            while ( t instanceof IASTDeclarator )
                t = t.getParent();
            if( t instanceof IASTDeclaration )
            {
                if( getParent instanceof IASTFunctionDefinition )
                    return r_definition;
                if( getParent instanceof IASTSimpleDeclaration )
                {
                    if( getInitializer() != null )
                        return r_definition;
                    IASTSimpleDeclaration sd = (IASTSimpleDeclaration) getParent;
                    int storage = sd.getDeclSpecifier().getStorageClass();
                    if( storage == IASTDeclSpecifier.sc_extern || 
                        storage == IASTDeclSpecifier.sc_typedef ||
                        storage == IASTDeclSpecifier.sc_static )
                    {
                        return r_declaration;
                    }
                }
                return fnDtor ? r_declaration : r_definition;                    
            }
            if( t instanceof IASTTypeId )
                return r_reference;
        }
        if( getParent instanceof IASTParameterDeclaration )
            return ( n.toCharArray().length > 0 ) ? r_definition : r_declaration;
        
        return r_unclear;
	}
}
