/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.model;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Verify.verify;
import static java.util.Arrays.stream;

/**
 * @author Alex Tymchenko
 */
public final class MethodSignatures {

    /**
     * Prevents this utility class from instantiation.
     */
    private MethodSignatures() {
    }

    public static boolean consistsOfSingle(Class<?>[] parameters, Class<?> type) {
        checkNotNull(parameters);
        checkNotNull(type);

        if (!(1 == parameters.length)) {
            return false;
        }
        Class<?> firstParam = parameters[0];
        return type.isAssignableFrom(firstParam);
    }

    public static boolean consistsOfTwo(Class<?>[] parameters,
                                        Class<?> firstType,
                                        Class<?> secondType) {
        checkNotNull(parameters);
        checkNotNull(firstType);
        checkNotNull(secondType);

        if (!(2 == parameters.length)) {
            return false;
        }
        Class<?> firstParam = parameters[0];
        Class<?> secondParam = parameters[1];
        return firstType.isAssignableFrom(firstParam) &&
                secondType.isAssignableFrom(secondParam);
    }

    public static boolean consistsOfTypes(Class<?>[] methodParams,
                                          List<Class<?>> expectedTypes) {
        if (methodParams.length != expectedTypes.size()) {
            return false;
        }
        for (int i = 0; i < methodParams.length; i++) {
            Class<?> actual = methodParams[i];
            Class<?> expected = expectedTypes.get(i);
            if (!expected.isAssignableFrom(actual)) {
                return false;
            }
        }
        return true;
    }

    static <S extends MethodSignature<?>>
    Optional<S> findMatching(Method method, Class<S> signatureClass) {

        Class<?>[] parameterTypes = method.getParameterTypes();
        S[] signatures = signatureClass.getEnumConstants();
        verify(signatures != null,
               "`MethodSignature` implementations are expected " +
                       "to be enumerations, but that's not true for %s",
               signatureClass);

        Optional<S> result = stream(signatures)
                .filter(s -> s.matches(parameterTypes))
                .findFirst();
        return result;
    }
}

@@ -1517,25 +1517,27 @@
 ss%3C?%3E%5B%5D 
-parameter
+methodParam
 s, Class
@@ -1565,33 +1565,35 @@
 heckNotNull(
-parameter
+methodParam
 s);%0A        
@@ -1628,33 +1628,35 @@
  if (!(1 == 
-parameter
+methodParam
 s.length)) %7B
@@ -1718,33 +1718,35 @@
 irstParam = 
-parameter
+methodParam
 s%5B0%5D;%0A      
@@ -1847,25 +1847,27 @@
 ss%3C?%3E%5B%5D 
-parameter
+methodParam
 s,%0A     
@@ -2005,25 +2005,27 @@
 NotNull(
-parameter
+methodParam
 s);%0A    
@@ -2107,25 +2107,27 @@
 (!(2 == 
-parameter
+methodParam
 s.length
@@ -2197,25 +2197,27 @@
 Param = 
-parameter
+methodParam
 s%5B0%5D;%0A  
@@ -2245,25 +2245,27 @@
 Param = 
-parameter
+methodParam
 s%5B1%5D;%0A  

Rename method parameters to be uniform.