/*******************************************************************************
 * Copyright (c) 2008, 2011 Wind River Systems, Inc. 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:
 *     Markus Schorn - initial API and implementation
 *******************************************************************************/ 
package org.eclipse.cdt.internal.core.dom.parser;

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IValue;

/**
 * Base class for C and C++ enumerators.
 */
public abstract class ASTEnumerator extends ASTNode implements IASTEnumerator, IASTAmbiguityParent {
    private IASTName name;
    private IASTExpression value;
    private IValue integralValue;

    public ASTEnumerator() {
	}

	public ASTEnumerator(IASTName name, IASTExpression value) {
		setName(name);
		setValue(value);
	}
	
	protected void copyAbstractEnumerator(ASTEnumerator copy, CopyStyle style) {
		copy.setName(name == null ? null : name.copy(style));
		copy.setValue(value == null ? null : value.copy(style));
		copy.setOffsetAndLength(this);
	}

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

    public IASTName getName() {
        return name;
    }

    public void setValue(IASTExpression expression) {
    	assertNotFrozen();
        this.value = expression;
        if (expression != null) {
			expression.setParent(this);
			expression.setPropertyInParent(ENUMERATOR_VALUE);
		}
    }

    public IASTExpression getValue() {
        return value;
    }

    @Override
	public boolean accept(ASTVisitor action) {
        if (action.shouldVisitEnumerators) {
		    switch (action.visit(this)) {
	            case ASTVisitor.PROCESS_ABORT: return false;
	            case ASTVisitor.PROCESS_SKIP: return true;
	            default: break;
	        }
		}
        if (name != null && !name.accept(action)) return false;
        if (value != null && !value.accept(action)) return false;
        if (action.shouldVisitEnumerators) {
		    switch (action.leave(this)) {
	            case ASTVisitor.PROCESS_ABORT: return false;
	            case ASTVisitor.PROCESS_SKIP: return true;
	            default: break;
	        }
		}
        return true;
    }

	public int getRoleForName(IASTName n) {
		if (n == name)
			return r_definition;
		
		return r_reference;
	}

    public void replace(IASTNode child, IASTNode other) {
        if (child == value) {
            other.setPropertyInParent(child.getPropertyInParent());
            other.setParent(child.getParent());
            value = (IASTExpression) other;
        }
    }

	public IValue getIntegralValue() {
		if (integralValue == null) {
			IASTNode parent= getParent();
			if (parent instanceof IASTInternalEnumerationSpecifier) {
				IASTInternalEnumerationSpecifier ies= (IASTInternalEnumerationSpecifier) parent;
				if (ies.startValueComputation()) { // prevents infinite recursions
					createEnumValues((IASTEnumerationSpecifier) parent);
				}
			}		
			if (integralValue == null) {
				integralValue= Value.UNKNOWN;
			}
		}
		return integralValue;
	}

	private void createEnumValues(IASTEnumerationSpecifier parent) {
		IASTEnumerator[] etors= parent.getEnumerators();
		long cv= -1;
		boolean isknown= true;
		for (IASTEnumerator etor : etors) {
			cv++;
			IASTExpression expr= etor.getValue();
			if (expr != null) {
				IValue val= Value.create(expr, Value.MAX_RECURSION_DEPTH);
				Long nv= val.numericalValue();
				isknown= false;
				if (nv != null) {
					isknown= true;
					cv= nv.longValue();
				}
			}
			if (etor instanceof ASTEnumerator) {
				((ASTEnumerator) etor).integralValue=  isknown ? Value.create(cv) : Value.UNKNOWN;
			}
		}
	}
}
