/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.benchmark
 */
package net.ranides.assira.reflection.util;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import net.ranides.benchmark.app.BenchmarkName;
import net.ranides.benchmark.app.BenchmarkRunner;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.RunnerException;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class ClassTraitsBenchmark {
    
    private static final Class<?>[] TYPES = new Class<?>[]{
        Byte.class,
        Short.class,
        Integer.class,
        Long.class,
        Character.class,
        Float.class,
        Double.class,
        Boolean.class,
        Void.class,
        int.class,
        String.class,
        Exception.class,
        CharSequence.class,
        Object.class,
        char.class,
        Number.class,
    };
    
    @Benchmark
    @BenchmarkName("isBoxed: HashMap")
    public int isBoxed_HashMap() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isBoxed_HashMap(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isBoxed: HashMap (assira)")
    public int isBoxed_AssiraHashMap() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isBoxed_AssiraHashMap(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isBoxed: switch")
    public int isBoxed_switch() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isBoxed_switch(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isBoxed: if")
    public int isBoxed_if() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isBoxed_if(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isPrimitive: if")
    public int isPrimitive_if() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isPrimitive_if(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isPrimitive: switch")
    public int isPrimitive_switch() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isPrimitive_switch(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isPrimitive: reflective")
    public int isPrimitive_reflective() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isPrimitive_reflective(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isPrimitive: HashMap")
    public int isPrimitive_HashMap() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isPrimitive_HashMap(type) ? 7 : 19;
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("isPrimitive: HashMap (assira)")
    public int isPrimitive_AssiraHashMap() {
        int ret = 0;
        for(Class<?> type : TYPES) {
            ret += isPrimitive_AssiraHashMap(type) ? 7 : 19;
        }
        return ret;
    }
    
    private static final Map<Class<?>,Class<?>> JDK_BOXED_2_PRIM = new HashMap<>();
    
    private static final Map<Class<?>,Class<?>> JDK_PRIM_2_BOXED = new HashMap<>();
    
    private static final Map<Class<?>,Class<?>> ASSIRA_BOXED_2_PRIM = new net.ranides.assira.collection.maps.HashMap<>();
    private static final Map<Class<?>,Class<?>> ASSIRA_PRIM_2_BOXED = new net.ranides.assira.collection.maps.HashMap<>();
    static {
        JDK_BOXED_2_PRIM.put(Byte.class, byte.class);
        JDK_BOXED_2_PRIM.put(Short.class, short.class);
        JDK_BOXED_2_PRIM.put(Integer.class, int.class);
        JDK_BOXED_2_PRIM.put(Long.class, long.class);
        JDK_BOXED_2_PRIM.put(Character.class, char.class);
        JDK_BOXED_2_PRIM.put(Float.class, float.class);
        JDK_BOXED_2_PRIM.put(Double.class, double.class);
        JDK_BOXED_2_PRIM.put(Boolean.class, boolean.class);
        JDK_BOXED_2_PRIM.put(Void.class, void.class);
        
        JDK_PRIM_2_BOXED.put(byte.class, Byte.class);
        JDK_PRIM_2_BOXED.put(short.class, Short.class);
        JDK_PRIM_2_BOXED.put(int.class, Integer.class);
        JDK_PRIM_2_BOXED.put(long.class, Long.class);
        JDK_PRIM_2_BOXED.put(char.class, Character.class);
        JDK_PRIM_2_BOXED.put(float.class, Float.class);
        JDK_PRIM_2_BOXED.put(double.class, Double.class);
        JDK_PRIM_2_BOXED.put(boolean.class, Boolean.class);
        JDK_PRIM_2_BOXED.put(void.class, Void.class);
        
        ASSIRA_BOXED_2_PRIM.putAll(JDK_BOXED_2_PRIM);
        ASSIRA_PRIM_2_BOXED.putAll(JDK_PRIM_2_BOXED);
    }
    
    public static boolean isBoxed_HashMap(Class<?> clazz) {
        return JDK_BOXED_2_PRIM.containsKey(clazz);
    }
    
    public static boolean isBoxed_AssiraHashMap(Class<?> clazz) {
        return ASSIRA_BOXED_2_PRIM.containsKey(clazz);
    }
    
    public static boolean isBoxed_switch(Class<?> clazz) {
        switch(clazz.getName()) {
            case "java.lang.Byte":
            case "java.lang.Short":
            case "java.lang.Integer":
            case "java.lang.Long":
            case "java.lang.Character":
            case "java.lang.Float":
            case "java.lang.Double":
            case "java.lang.Boolean":
            case "java.lang.Void":
                return true;
            default:
                return false;
        }
    }
    
    public static boolean isBoxed_if(Class<?> clazz) {
        return
            Byte.class.equals(clazz) ||
            Short.class.equals(clazz) ||
            Integer.class.equals(clazz) ||
            Long.class.equals(clazz) ||
            Character.class.equals(clazz) ||
            Float.class.equals(clazz) ||
            Double.class.equals(clazz) ||
            Boolean.class.equals(clazz) ||
            Void.class.equals(clazz);
    }
    
    public static boolean isPrimitive_switch(Class<?> clazz) {
        switch(clazz.getName()) {
            case "byte":
            case "short":
            case "int":
            case "long":
            case "char":
            case "float":
            case "double":
            case "boolean":
            case "void":
                return true;
            default:
                return false;
        }
    }
    
    public static boolean isPrimitive_if(Class<?> clazz) {
        return
            byte.class.equals(clazz) ||
            short.class.equals(clazz) ||
            int.class.equals(clazz) ||
            long.class.equals(clazz) ||
            char.class.equals(clazz) ||
            float.class.equals(clazz) ||
            double.class.equals(clazz) ||
            boolean.class.equals(clazz) ||
            void.class.equals(clazz);
    }
    
    public static boolean isPrimitive_reflective(Class<?> clazz) {
        return clazz.isPrimitive();
    }
    
    public static boolean isPrimitive_HashMap(Class<?> clazz) {
        return JDK_PRIM_2_BOXED.containsKey(clazz);
    }
    
    public static boolean isPrimitive_AssiraHashMap(Class<?> clazz) {
        return ASSIRA_PRIM_2_BOXED.containsKey(clazz);
    }
    
    public static void main(String[] args) throws IOException, RunnerException {
        BenchmarkRunner.run(ClassTraitsBenchmark.class);
    }
    
}
