/*
 * JaamSim Discrete Event Simulation
 * Copyright (C) 2013 Ausenco Engineering Canada Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jaamsim.basicsim;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.jaamsim.events.ProcessTarget;

public class ReflectionTarget extends ProcessTarget {
	private final Entity target; // The entity whose method is to be executed
	private final Method method; // The method to be executed
	private final Object[] arguments; // The arguments passed to the method to be executed

	public ReflectionTarget(Entity ent, String methodName, Object[] arguments) {
		target = ent;
		method = findEntityMethod(target.getClass(), methodName, arguments);
		this.arguments = arguments;
	}

	@Override
	public void process() {
		try {
			method.invoke(target, arguments);
		}
		// Normal exceptions thrown by the method called by invoke are wrapped
		catch (InvocationTargetException e) {
			Throwable cause = e.getCause();
			if (cause instanceof RuntimeException)
				throw (RuntimeException)cause;
			else
				throw new ErrorException(cause);
		}
		catch (IllegalArgumentException e) {
			throw e;
		}
		catch (IllegalAccessException e) {
			throw new ErrorException(e);
		}
	}

	@Override
	public String getDescription() {
		return String.format("%s.%s", target.getName(), method.getName());
	}

	// Look up the method with the given name for the given entity and argument list.
	private Method findEntityMethod(Class<?> targetClass, String methodName, Object... arguments) {
		Class<?>[] argClasses = new Class<?>[arguments.length];

		// Fill in the class of each argument, if there are any
		for (int i = 0; i < arguments.length; i++) {
			// The argument itself is null, no class information available
			if (arguments[i] == null) {
				argClasses[i] = null;
				continue;
			}

			argClasses[i] = arguments[i].getClass();

			// We wrap primitive doubles as Double, put back the primitive type
			if (argClasses[i] == Double.class) {
				argClasses[i] = Double.TYPE;
			}

			// We wrap primitive integers as Integer, put back the primitive type
			if (argClasses[i] == Integer.class) {
				argClasses[i] = Integer.TYPE;
			}
		}

		// Attempt to lookup the method using exact type information
		try {
			return targetClass.getMethod(methodName, argClasses);
		}
		catch (SecurityException e) {
			throw new ErrorException("Security Exception when finding method: %s", methodName);
		}
		catch (NullPointerException e) {
			throw new ErrorException("Name passed to startProcess was NULL");
		}
		catch (NoSuchMethodException e) {
			// Get a list of all our methods
			Method[] methods = targetClass.getMethods();

			// Loop over all methods looking for a unique method name
			int matchIndexHolder = -1;
			int numMatches = 0;
			for (int i = 0; i < methods.length; i++) {
				if (methods[i].getName().equals(methodName)) {
					numMatches++;
					matchIndexHolder = i;
				}
			}

			// If there was only one method found, use it
			if (numMatches == 1)
				return methods[matchIndexHolder];
			else
			throw new ErrorException("Method: %s does not exist, could not invoke.", methodName);
		}
	}
}

@@ -1143,26 +1143,27 @@
 Name, Object
-%5B%5D
+...
  arguments) 

fix the constructor for ReflectionTarget