/* *******************************************************************
 * Copyright (c) 2005 Contributors.
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *   Adrian Colyer			Initial implementation
 * ******************************************************************/
package org.aspectj.weaver.reflect;

import java.lang.reflect.Member;

import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ast.Var;

/**
 * A variable at a reflection shadow, used by the residual tests.
 */
public final class ReflectionVar extends Var {

	static final int THIS_VAR = 0;
	static final int TARGET_VAR = 1;
	static final int ARGS_VAR = 2;
	static final int AT_THIS_VAR = 3;
	static final int AT_TARGET_VAR = 4;
	static final int AT_ARGS_VAR = 5;
	static final int AT_WITHIN_VAR = 6;
	static final int AT_WITHINCODE_VAR = 7;
	static final int AT_ANNOTATION_VAR = 8;

	private AnnotationFinder annotationFinder = null;

//	static {
//		try {
//			Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder");
//			annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance();
//		} catch(ClassNotFoundException ex) {
//			// must be on 1.4 or earlier
//		} catch(IllegalAccessException ex) {
//			// not so good
//			throw new RuntimeException("AspectJ internal error",ex);
//		} catch(InstantiationException ex) {
//			throw new RuntimeException("AspectJ internal error",ex);
//		}
//	}

	private int argsIndex = 0;
	private int varType;

	public static ReflectionVar createThisVar(ResolvedType type,AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = THIS_VAR;
		return ret;
	}

	public static ReflectionVar createTargetVar(ResolvedType type, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = TARGET_VAR;
		return ret;
	}

	public static ReflectionVar createArgsVar(ResolvedType type, int index, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = ARGS_VAR;
		ret.argsIndex = index;
		return ret;
	}

	public static ReflectionVar createThisAnnotationVar(ResolvedType type, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = AT_THIS_VAR;
		return ret;
	}

	public static ReflectionVar createTargetAnnotationVar(ResolvedType type, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = AT_TARGET_VAR;
		return ret;
	}

	public static ReflectionVar createArgsAnnotationVar(ResolvedType type, int index, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(type,finder);
		ret.varType = AT_ARGS_VAR;
		ret.argsIndex = index;
		return ret;
	}

	public static ReflectionVar createWithinAnnotationVar(ResolvedType annType, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(annType,finder);
		ret.varType = AT_WITHIN_VAR;
		return ret;
	}

	public static ReflectionVar createWithinCodeAnnotationVar(ResolvedType annType, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(annType,finder);
		ret.varType = AT_WITHINCODE_VAR;
		return ret;
	}

	public static ReflectionVar createAtAnnotationVar(ResolvedType annType, AnnotationFinder finder) {
		ReflectionVar ret = new ReflectionVar(annType,finder);
		ret.varType = AT_ANNOTATION_VAR;
		return ret;
	}

	private ReflectionVar(ResolvedType type,AnnotationFinder finder) {
		super(type);
		this.annotationFinder = finder;
	}


	public Object getBindingAtJoinPoint(Object thisObject, Object targetObject, Object[] args) {
		return getBindingAtJoinPoint(thisObject,targetObject,args,null,null,null);
	}
	/**
	 * At a join point with the given this, target, and args, return the object to which this
	 * var is bound.
	 * @param thisObject
	 * @param targetObject
	 * @param args
	 * @return
	 */
	public Object getBindingAtJoinPoint(
			Object thisObject,
			Object targetObject,
			Object[] args,
			Member subject,
			Member withinCode,
			Class withinType) {
		switch( this.varType) {
		case THIS_VAR: return thisObject;
		case TARGET_VAR: return targetObject;
		case ARGS_VAR:
			if (this.argsIndex > (args.length - 1)) return null;
			return args[argsIndex];
		case AT_THIS_VAR:
			if (annotationFinder != null) {
				return annotationFinder.getAnnotation(getType(), thisObject);
			} else return null;
		case AT_TARGET_VAR:
			if (annotationFinder != null) {
				return annotationFinder.getAnnotation(getType(), targetObject);
			} else return null;
		case AT_ARGS_VAR:
			if (this.argsIndex > (args.length - 1)) return null;
			if (annotationFinder != null) {
				return annotationFinder.getAnnotation(getType(), args[argsIndex]);
			} else return null;
		case AT_WITHIN_VAR:
			if (annotationFinder != null) {
				return annotationFinder.getAnnotationFromClass(getType(), withinType);
			} else return null;
		case AT_WITHINCODE_VAR:
			if (annotationFinder != null) {
				return annotationFinder.getAnnotationFromMember(getType(), withinCode);
			} else return null;
		case AT_ANNOTATION_VAR:
			if (annotationFinder != null) {
				return annotationFinder.getAnnotationFromMember(getType(), subject);
			} else return null;
		}

		return null;
	}

}
