/*
 *
 * Copyright 2018
 *
 * 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 io.github.resilience4j.feign;

import static feign.Util.checkNotNull;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import feign.InvocationHandlerFactory.MethodHandler;
import feign.Target;
import io.vavr.CheckedFunction1;

/**
 * An instance of {@link InvocationHandler} that uses {@link FeignDecorator}s to enhance the
 * invocations of methods.
 */
class DecoratorInvocationHandler implements InvocationHandler {

    private final Target<?> target;
    private final Map<Method, CheckedFunction1<Object[], Object>> decoratedDispatch;

    public DecoratorInvocationHandler(Target<?> target,
            Map<Method, MethodHandler> dispatch,
            FeignDecorator invocationDecorator) {
        this.target = checkNotNull(target, "target");
        checkNotNull(dispatch, "dispatch");
        this.decoratedDispatch = decorateMethodHandlers(dispatch, invocationDecorator, target);
    }

    /**
     * Applies the {@link FeignDecorator} to all specified {@link MethodHandler}s and returns the
     * result as a map of {@link CheckedFunction1}s. Thus, invoking a {@link CheckedFunction1} will
     * call the decorators backed by the underlining {@link MethodHandler}.
     */
    private Map<Method, CheckedFunction1<Object[], Object>> decorateMethodHandlers(Map<Method, MethodHandler> dispatch,
            FeignDecorator invocationDecorator, Target<?> target) {
        final Map<Method, CheckedFunction1<Object[], Object>> map = new HashMap<>();
        for (final Map.Entry<Method, MethodHandler> entry : dispatch.entrySet()) {
            final Method method = entry.getKey();
            final MethodHandler methodHandler = entry.getValue();
            map.put(method, invocationDecorator.decorate(methodHandler::invoke, method, methodHandler, target));
        }
        return map;
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args)
            throws Throwable {
        switch (method.getName()) {
            case "equals":
                return equals(args.length > 0 ? args[0] : null);

            case "hashCode":
                return hashCode();

            case "toString":
                return toString();

            default:
                break;
        }

        return decoratedDispatch.get(method).apply(args);
    }

    @Override
    public boolean equals(Object obj) {
        Object compareTo = obj;
        if (compareTo == null) {
            return false;
        }
        if (Proxy.isProxyClass(compareTo.getClass())) {
            compareTo = Proxy.getInvocationHandler(compareTo);
        }
        if (compareTo instanceof DecoratorInvocationHandler) {
            final DecoratorInvocationHandler other = (DecoratorInvocationHandler) compareTo;
            return target.equals(other.target);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return target.hashCode();
    }

    @Override
    public String toString() {
        return target.toString();
    }
}

@@ -1618,16 +1618,26 @@
 ies the 
+specified 
 %7B@link F
@@ -1694,16 +1694,23 @@
 er%7Ds and
+%0A     *
  returns
@@ -1713,23 +1713,16 @@
 urns the
-%0A     *
  result 
@@ -1764,15 +1764,9 @@
 %7Ds. 
-Thus, i
+I
 nvok
@@ -1799,21 +1799,16 @@
 on1%7D
- will
 %0A     * 
 call
@@ -1807,53 +1807,95 @@
   * 
-ca
+wi
 ll the
- decorators backed by the underlining
+refore invoke the decorator which, in turn, may invoke the corresponding%0A     *
  %7B@l

Clean up of javadoc