/*
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */
package com.sun.jna;

import java.lang.reflect.Method;

/**
 * Class for checking if a method has vararg parameters.
 * Use method {@link VarArgsChecker#create() create()} to create an instance
 * of this class. How the returned instance work depends on the capabilities
 * of the underlying JVM implementation. On older versions of the VM not supporting
 * varargs, the returned VarArgsChecker will always return <code>false</code>
 * on calls to {@link VarArgsChecker#isVarArgs(Method) isVarArgs(Method)}.
 * @author Max Bureck
 */
abstract class VarArgsChecker {

    private VarArgsChecker() {
    }

    /**
     * Implementation actually using Method.isVarArgs()
     */
    private static final class RealVarArgsChecker extends VarArgsChecker {

        boolean isVarArgs(Method m) {
            return m.isVarArgs();
        }

        int fixedArgs(Method m) {
            // In Java, final argument contains all "varargs"
            return m.isVarArgs() ? m.getParameterTypes().length - 1 : 0;
        }
    }

    /**
     * Implementation always returning false when {@link NoVarArgsChecker#isVarArgs(Method)}
     * is called. This implementation will be chosen, if {@link Method#isVarArgs()} is not available
     */
    private static final class NoVarArgsChecker extends VarArgsChecker {

        boolean isVarArgs(Method m) {
            return false;
        }

        int fixedArgs(Method m) {
            return 0;
        }
    }

    /**
     * Creates a new instance of a concrete subclass of VarArgsChecker, depending
     * if {@link Method#isVarArgs()} exists.
     * @return new instance of concrete VarArgsChecker subclass
     */
    static VarArgsChecker create() {
        try {
            // check if Method#isVarArgs() exists
            final Method isVarArgsMethod = Method.class.getMethod("isVarArgs", new Class[0]);
            if(isVarArgsMethod != null) {
                // if it exitsts, return new instance of RealVarArgsChecker
                return new RealVarArgsChecker();
            } else {
                return new NoVarArgsChecker();
            }
        } catch (NoSuchMethodException | SecurityException e) {
            return new NoVarArgsChecker();
        }
    }

    /**
     * Checks if the given method was declared to take a variable number of arguments.
     * @param m Method to be checked
     * @return <code>true</code> if the given method takes a variable number of arguments, <code>false</code> otherwise.
     */
    abstract boolean isVarArgs(Method m);

    /**
     * If variadic, returns the number of fixed arguments to the method.
     * @param m Method to be checked
     * @return Number of fixed arguments if the given method takes a variable number of arguments, zero otherwise.
     */
    abstract int fixedArgs(Method m);
}
