/*******************************************************************************
 * Copyright (c) 2007, 2008 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.parser.scanner;

import java.util.ArrayList;

import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;

/**
 * A location context representing macro expansions.
 * @since 5.0
 */
class LocationCtxMacroExpansion extends LocationCtx {
	private final LocationMap fLocationMap;
	private final int fLength;
	private final ImageLocationInfo[] fLocationInfos;
	private ASTMacroReferenceName fExpansionName;

	public LocationCtxMacroExpansion(LocationMap map, LocationCtxContainer parent, int parentOffset, int parentEndOffset,
			int sequenceNumber, int length, ImageLocationInfo[] imageLocations,	ASTMacroReferenceName expansionName) {
		super(parent, parentOffset, parentEndOffset, sequenceNumber);
		fLocationMap= map;
		fLength= length;
		fLocationInfos= imageLocations;
		fExpansionName= expansionName;
		if (expansionName.getParent() instanceof ASTMacroExpansion == false) {
			throw new IllegalArgumentException(expansionName.toString() + " is not a macro expansion name"); //$NON-NLS-1$
		}
	}

	@Override
	public int getSequenceLength() {
		return fLength;
	}
	
	@Override
	public boolean collectLocations(int start, int length, ArrayList<IASTNodeLocation> locations) {
		final int offset= start-fSequenceNumber;
		assert offset >= 0 && length >= 0;
		
		if (offset+length <= fLength) {
			locations.add(new ASTMacroExpansionLocation(this, offset, length));
			return true;
		}

		locations.add(new ASTMacroExpansionLocation(this, offset, fLength-offset));
		return false;
	}	
	
	public ASTMacroExpansion getExpansion() {
		return (ASTMacroExpansion) fExpansionName.getParent();
	}
	
	public ASTMacroReferenceName getMacroReference() {
		return fExpansionName;
	}
	
	public IASTPreprocessorMacroDefinition getMacroDefinition() {
		return fLocationMap.getMacroDefinition((IMacroBinding) fExpansionName.getBinding());
	}
	
	@Override
	public LocationCtxMacroExpansion findEnclosingMacroExpansion(int sequenceNumber, int length) {
		return this;
	}

	public IASTImageLocation getImageLocation(int offset, int length) {
		if (length == 0) {
			return null;
		}
		final int end= offset+length;
		int nextToCheck= offset;
		ImageLocationInfo firstInfo= null;
		ImageLocationInfo lastInfo= null;
		for (ImageLocationInfo info : fLocationInfos) {
			if (info.fTokenOffsetInExpansion == nextToCheck) {
				if (firstInfo == null || lastInfo == null) {
					firstInfo= lastInfo= info;
				}
				else if (lastInfo.canConcatenate(info)) {
					lastInfo= info;
				}
				else {
					return null;
				}
				if (++nextToCheck == end) {
					return firstInfo.createLocation(fLocationMap, lastInfo);
				}
			}
			else if (info.fTokenOffsetInExpansion > nextToCheck) {
				return null;
			}
		}
		return null;
	}

	public ASTPreprocessorName[] getNestedMacroReferences() {
		return fLocationMap.getNestedMacroReferences((ASTMacroExpansion) fExpansionName.getParent());
	}
}


