/*******************************************************************************
 * Copyright (c) 2006, 2008 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 Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.lrparser.c99.action.deprecated;

import static org.eclipse.cdt.internal.core.dom.lrparser.c99.C99Parsersym.*;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.c.ICBasicType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.C99Parsersym;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99BasicType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Function;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99QualifierType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Variable;

/**
 * Keeps track of declaration specifiers during the parse.
 * Used to compute types and determine if a declarator is a typedef.
 * 
 * @author Mike Kucera
 */
class DeclSpec {
	
	// maps token kinds to the number of occurrences of that kind
	private Map<Integer,Integer> tokenKindMap = new HashMap<Integer,Integer>();

	private IType type = null;
	
	
	public void add(int kind) {
		tokenKindMap.put(kind, count(kind) + 1);
	}
	
	public void remove(final int kind) {
		Integer count = tokenKindMap.get(kind);
		if(count == null)
			return;
		
		if(count <= 1)
			tokenKindMap.remove(kind);
		else
			tokenKindMap.put(kind, count - 1);
	}
	
	public boolean contains(int kind) {
		return tokenKindMap.containsKey(kind);
	}
	

	public boolean isTypedef() {
		return contains(C99Parsersym.TK_typedef);
	}
	
	/**
	 * Need to keep track of how many times a particular
	 * declaration specifier appears in order to support
	 * long long.
	 */
	public int count(int kind) {
		Integer count = tokenKindMap.get(kind);
		return count == null ? 0 : count;
	}
	
	/**
	 * Set if the type should be a structure.
	 */
	public void setType(IType type) {
		this.type = type;
	}
	
	

	public IType getType() {
		if(type != null)
			return type;
		if(tokenKindMap.isEmpty()) // there are no type tokens, so it must be implicit int
			return new C99BasicType(IBasicType.t_int);
		
		C99BasicType basicType = new C99BasicType();
		
		for(int kind : tokenKindMap.keySet()) {
			switch(kind) {
				case TK_void:
					basicType.setType(IBasicType.t_void);
					break;
				case TK_char:
					basicType.setType(IBasicType.t_char);
					break;
				case TK_int:
					basicType.setType(IBasicType.t_int);
					break;
				case TK_float:
					basicType.setType(IBasicType.t_float);
					break;
				case TK_double:
					basicType.setType(IBasicType.t_double);
					break;
				case TK_long:
					boolean isLongLong = count(TK_long) > 1;
					basicType.setLongLong(isLongLong);
					basicType.setLong(!isLongLong);
					break;
				case TK_signed:
					basicType.setSigned(true);
					break;
				case TK_unsigned:
					basicType.setUnsigned(true);
					break;
				case TK_short:
					basicType.setShort(true);
					break;
				case TK__Bool:
					basicType.setType(ICBasicType.t_Bool);
					break;
				case TK__Complex:
					basicType.setComplex(true);
					break;
				case TK__Imaginary:
					basicType.setImaginary(true);
					break;
			}
		}
		
		boolean isConst    = contains(TK_const);
		boolean isRestrict = contains(TK_restrict);
		boolean isVolatile = contains(TK_volatile);
		
		if(isConst || isRestrict || isVolatile)
			return new C99QualifierType(basicType, isConst, isVolatile, isRestrict);
		return basicType;
	}


	public void modifyBinding(C99Variable var) {
		if(!var.isAuto())
			var.setAuto(contains(TK_auto));
		if(!var.isExtern())
			var.setExtern(contains(TK_extern));
		if(!var.isRegister())
			var.setRegister(contains(TK_register));
		if(!var.isStatic())
			var.setStatic(contains(TK_static));
	}
	
	public void modifyBinding(C99Function function) {
		if(!function.isAuto())
			function.setAuto(contains(TK_auto));
		if(!function.isExtern())
			function.setExtern(contains(TK_extern));
		if(!function.isInline())
			function.setInline(contains(TK_inline));
		if(!function.isRegister())
			function.setRegister(contains(TK_register));
		if(!function.isStatic())
			function.setStatic(contains(TK_static));
	}
}
