/*
 * generated by Xtext 2.10.0
 */
package org.etsi.mts.tdl.graphical.labels.scoping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
import org.eclipse.xtext.scoping.impl.SimpleScope;
import org.etsi.mts.tdl.DataInstanceUse;
import org.etsi.mts.tdl.DataType;
import org.etsi.mts.tdl.DataUse;
import org.etsi.mts.tdl.FormalParameterUse;
import org.etsi.mts.tdl.FunctionCall;
import org.etsi.mts.tdl.Member;
import org.etsi.mts.tdl.MemberReference;
import org.etsi.mts.tdl.ParameterBinding;
import org.etsi.mts.tdl.PredefinedFunctionCall;
import org.etsi.mts.tdl.SpecialValueUse;
import org.etsi.mts.tdl.StructuredDataType;
import org.etsi.mts.tdl.VariableUse;

import com.google.inject.Inject;

/**
 * This class contains custom scoping description.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
 * on how and when to use it.
 */
public class DataScopeProvider extends AbstractDeclarativeScopeProvider {

	@Inject
	private IQualifiedNameProvider qualifiedNameProvider;
	
	public IScope scope_ParameterBinding_parameter(ParameterBinding parameterBinding, EReference ref) {
		DataUse dataUse = (DataUse) parameterBinding.eContainer();
		return getDataUseMembers(dataUse);
	}
	
	public IScope scope_MemberReference_member(MemberReference memberRef, EReference ref) {
		DataUse dataUse = (DataUse) memberRef.eContainer();
		return getDataUseMembers(dataUse);
	}
	
	private IScope getDataUseMembers(DataUse dataUse) {
		List<IEObjectDescription> descriptions;
		
		DataType type = getDataUseType(dataUse);
		if (type instanceof StructuredDataType) {
			descriptions = new ArrayList<IEObjectDescription>();
			gatherMembers((StructuredDataType) type, descriptions);
		} else
			descriptions = Collections.EMPTY_LIST;
		
		SimpleScope scope = new QNameShadowingScope(descriptions);
		return scope;
	}
	
	private DataType getDataUseType(DataUse dataUse) {
		if (dataUse instanceof DataInstanceUse)
			return ((DataInstanceUse) dataUse).getDataInstance().getDataType();
		else if (dataUse instanceof VariableUse)
			return ((VariableUse) dataUse).getVariable().getDataType();
		else if (dataUse instanceof SpecialValueUse)
			//TODO didn't this have a type?
			return null;
		else if (dataUse instanceof FormalParameterUse)
			((FormalParameterUse) dataUse).getParameter().getDataType();
		else if (dataUse instanceof FunctionCall)
			((FunctionCall) dataUse).getFunction().getReturnType();
		else if (dataUse instanceof PredefinedFunctionCall)
			((PredefinedFunctionCall) dataUse).getFunction().getReturnType();
		return null;
	}
	
	private void gatherMembers(StructuredDataType type, List<IEObjectDescription> descriptions) {
		attributes: for (Member m : type.getMember()) {
			DifferringQNameEObjectDescription desc = new DifferringQNameEObjectDescription(qualifiedNameProvider.getFullyQualifiedName(m), m);
			QualifiedName qName = desc.getQualifiedName();
			for (IEObjectDescription d : descriptions)
				if (((DifferringQNameEObjectDescription)d).getQualifiedName().equals(qName))
					continue attributes;
			descriptions.add(desc);
		}
	}
	
	class DifferringQNameEObjectDescription extends EObjectDescription {
		
		private QualifiedName name;

		public DifferringQNameEObjectDescription(QualifiedName qualifiedName, EObject element) {
			super(qualifiedName, element, Collections.<String,String>emptyMap());
			name = QualifiedName.create(qualifiedName.getLastSegment());
		}
		
		@Override
		public QualifiedName getName() {
			return name;
		}
	}

	class QNameShadowingScope extends SimpleScope {

		public QNameShadowingScope(Iterable<IEObjectDescription> descriptions) {
			super(descriptions);
		}
		
		@Override
		protected Object getShadowingKey(IEObjectDescription description) {
			return description.getQualifiedName();
		}
		
	}
}