// 
// Decompiled by Procyon v0.6.0
// 

package org.jline.style;

import org.jline.utils.AttributedStyle;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import javax.annotation.Nullable;
import org.jline.utils.AttributedString;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.logging.Logger;
import java.lang.reflect.InvocationHandler;

class StyleBundleInvocationHandler implements InvocationHandler
{
    private static final Logger log;
    private final Class<? extends StyleBundle> type;
    private final StyleResolver resolver;
    
    public StyleBundleInvocationHandler(final Class<? extends StyleBundle> type, final StyleResolver resolver) {
        this.type = Objects.requireNonNull(type);
        this.resolver = Objects.requireNonNull(resolver);
    }
    
    private static void validate(final Method method) {
        if (method.getParameterCount() != 1) {
            throw new InvalidStyleBundleMethodException(method, "Invalid parameters");
        }
        if (method.getReturnType() != AttributedString.class) {
            throw new InvalidStyleBundleMethodException(method, "Invalid return-type");
        }
    }
    
    @Nullable
    private static String emptyToNull(@Nullable final String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        return value;
    }
    
    @Nullable
    private static String getStyleGroup(final Class<?> type) {
        final StyleBundle.StyleGroup styleGroup = type.getAnnotation(StyleBundle.StyleGroup.class);
        return (styleGroup != null) ? emptyToNull(styleGroup.value().trim()) : null;
    }
    
    private static String getStyleName(final Method method) {
        final StyleBundle.StyleName styleName = method.getAnnotation(StyleBundle.StyleName.class);
        return (styleName != null) ? emptyToNull(styleName.value().trim()) : method.getName();
    }
    
    @Nullable
    private static String getDefaultStyle(final Method method) {
        final StyleBundle.DefaultStyle defaultStyle = method.getAnnotation(StyleBundle.DefaultStyle.class);
        return (defaultStyle != null) ? emptyToNull(defaultStyle.value()) : null;
    }
    
    static <T extends StyleBundle> T create(final StyleResolver resolver, final Class<T> type) {
        Objects.requireNonNull(resolver);
        Objects.requireNonNull(type);
        if (StyleBundleInvocationHandler.log.isLoggable(Level.FINEST)) {
            StyleBundleInvocationHandler.log.finest(String.format("Using style-group: %s for type: %s", resolver.getGroup(), type.getName()));
        }
        final StyleBundleInvocationHandler handler = new StyleBundleInvocationHandler(type, resolver);
        return (T)Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, handler);
    }
    
    static <T extends StyleBundle> T create(final StyleSource source, final Class<T> type) {
        Objects.requireNonNull(type);
        final String group = getStyleGroup(type);
        if (group == null) {
            throw new InvalidStyleGroupException(type);
        }
        return create(new StyleResolver(source, group), type);
    }
    
    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
        }
        validate(method);
        final String styleName = getStyleName(method);
        String style = this.resolver.getSource().get(this.resolver.getGroup(), styleName);
        if (StyleBundleInvocationHandler.log.isLoggable(Level.FINEST)) {
            StyleBundleInvocationHandler.log.finest(String.format("Sourced-style: %s -> %s", styleName, style));
        }
        if (style == null) {
            style = getDefaultStyle(method);
            if (style == null) {
                throw new StyleBundleMethodMissingDefaultStyleException(method);
            }
        }
        final String value = String.valueOf(args[0]);
        if (StyleBundleInvocationHandler.log.isLoggable(Level.FINEST)) {
            StyleBundleInvocationHandler.log.finest(String.format("Applying style: %s -> %s to: %s", styleName, style, value));
        }
        final AttributedStyle astyle = this.resolver.resolve(style);
        return new AttributedString(value, astyle);
    }
    
    @Override
    public String toString() {
        return this.type.getName();
    }
    
    static {
        log = Logger.getLogger(StyleBundleInvocationHandler.class.getName());
    }
    
    static class StyleBundleMethodMissingDefaultStyleException extends RuntimeException
    {
        private static final long serialVersionUID = 1L;
        
        public StyleBundleMethodMissingDefaultStyleException(final Method method) {
            super(String.format("%s method missing @%s: %s", StyleBundle.class.getSimpleName(), StyleBundle.DefaultStyle.class.getSimpleName(), method));
        }
    }
    
    static class InvalidStyleBundleMethodException extends RuntimeException
    {
        private static final long serialVersionUID = 1L;
        
        public InvalidStyleBundleMethodException(final Method method, final String message) {
            super(message + ": " + method);
        }
    }
    
    static class InvalidStyleGroupException extends RuntimeException
    {
        private static final long serialVersionUID = 1L;
        
        public InvalidStyleGroupException(final Class<?> type) {
            super(String.format("%s missing or invalid @%s: %s", StyleBundle.class.getSimpleName(), StyleBundle.StyleGroup.class.getSimpleName(), type.getName()));
        }
    }
}
