/*******************************************************************************
 * Copyright (c) 2013 Jesper S Moller and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Jesper S Moller <jesper@selskabet.org> - initial API and implementation
 ********************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;

public class ContainerAnnotation extends SingleMemberAnnotation {
	
	private Annotation [] containees;
	private ArrayInitializer memberValues;
	
	public ContainerAnnotation(Annotation repeatingAnnotation, ReferenceBinding containerAnnotationType, BlockScope scope) {
		
		char [][] containerTypeName = containerAnnotationType.compoundName;
		if (containerTypeName.length == 1) {
			this.type = new SingleTypeReference(containerTypeName[0], 0);
		} else {
			this.type = new QualifiedTypeReference(containerTypeName, new long [containerTypeName.length]);
		}
		
		this.sourceStart = repeatingAnnotation.sourceStart;
		this.sourceEnd = repeatingAnnotation.sourceEnd;
		
		this.resolvedType = containerAnnotationType;
		this.recipient = repeatingAnnotation.recipient;
		this.containees = new Annotation[0];
		this.memberValue = this.memberValues = new ArrayInitializer();
		addContainee(repeatingAnnotation);
	}
	
	public void addContainee(Annotation repeatingAnnotation) {
		final int length = this.containees.length;
		System.arraycopy(this.containees, 0, this.containees = new Annotation[length + 1], 0, length);
		this.containees[length] = repeatingAnnotation;
		this.memberValues.expressions = this.containees;
		repeatingAnnotation.setPersistibleAnnotation(length == 0 ? this : null);
	}
	
	// Resolve the compiler synthesized container annotation.
	@Override
	public TypeBinding resolveType(BlockScope scope) {

		if (this.compilerAnnotation != null)
			return this.resolvedType;

		this.constant = Constant.NotAConstant;

		ReferenceBinding containerAnnotationType = (ReferenceBinding) this.resolvedType;
		if (!containerAnnotationType.isValidBinding())
			containerAnnotationType = (ReferenceBinding) containerAnnotationType.closestMatch();
		Annotation repeatingAnnotation = this.containees[0];
		ReferenceBinding repeatingAnnotationType = (ReferenceBinding) repeatingAnnotation.resolvedType;
		if (!repeatingAnnotationType.isDeprecated() && isTypeUseDeprecated(containerAnnotationType, scope)) {
			scope.problemReporter().deprecatedType(containerAnnotationType, repeatingAnnotation);
		}
		checkContainerAnnotationType(repeatingAnnotation, scope, containerAnnotationType, repeatingAnnotationType, true); // true => repeated *use* site error reporting requested.
		this.resolvedType = containerAnnotationType = repeatingAnnotationType.containerAnnotationType();
		if (!this.resolvedType.isValidBinding())
			return this.resolvedType;
		
		// OK, the declaration site of the repeating annotation type as well as the use site where the annotations actually repeat pass muster. 
		MethodBinding[] methods = containerAnnotationType.methods();
		MemberValuePair pair = memberValuePairs()[0];
		
		for (int i = 0, length = methods.length; i < length; i++) {
			MethodBinding method = methods[i];
			if (CharOperation.equals(method.selector, TypeConstants.VALUE)) {
				pair.binding = method;
				pair.resolveTypeExpecting(scope, method.returnType);
			}
		}
		this.compilerAnnotation = scope.environment().createAnnotation((ReferenceBinding) this.resolvedType, computeElementValuePairs());
		return this.resolvedType;
	}
}
