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

import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class ClassTraits {
    
    private static final int ACCESS_ANY = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
    
    private ClassTraits() {
        // utility class
    }

    public static boolean isPrimitive(Class<?> clazz) {
        return clazz.isPrimitive();
    }
    
    public static boolean isPrimitiveArray(Class<?> clazz) {
        return clazz.isArray() && isPrimitive(clazz.getComponentType());
    }

    public static boolean isStatic(Class<?> clazz) {
        return Modifier.isStatic(clazz.getModifiers());
    }

    public static boolean isPublic(Class<?> clazz) {
        return Modifier.isPublic(clazz.getModifiers());
    }

    public static boolean isProtected(Class<?> clazz) {
        return Modifier.isProtected(clazz.getModifiers());
    }
    
    public static boolean isPackage(Class<?> member) {
        return 0 == (member.getModifiers() & ACCESS_ANY);
    }

    public static boolean isPrivate(Class<?> clazz) {
        return Modifier.isPrivate(clazz.getModifiers());
    }

    public static boolean isInterface(Class<?> clazz) {
        return Modifier.isInterface(clazz.getModifiers());
    }

    public static boolean isFinal(Class<?> clazz) {
        return Modifier.isFinal(clazz.getModifiers());
    }

    public static boolean isAbstract(Class<?> clazz) {
        return Modifier.isAbstract(clazz.getModifiers());
    }
    
    public static boolean isBoxed(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 isBool(Class<?> clazz) {
        return boolean.class.equals(clazz) || Boolean.class.equals(clazz);
    }

    public static boolean isNumber(Class<?> clazz) {
        return
            byte.class.equals(clazz) ||
            short.class.equals(clazz) ||
            int.class.equals(clazz) ||
            long.class.equals(clazz) ||
            float.class.equals(clazz) ||
            double.class.equals(clazz) ||
            Byte.class.equals(clazz) ||
            Short.class.equals(clazz) ||
            Integer.class.equals(clazz) ||
            Long.class.equals(clazz) ||
            Float.class.equals(clazz) ||
            Double.class.equals(clazz) ||
            Number.class.isAssignableFrom(clazz);
    }
    
    public static boolean isInteger(Class<?> clazz) {
        return
            byte.class.equals(clazz) ||
            short.class.equals(clazz) ||
            int.class.equals(clazz) ||
            long.class.equals(clazz) ||
            Byte.class.equals(clazz) ||
            Short.class.equals(clazz) ||
            Integer.class.equals(clazz) ||
            Long.class.equals(clazz) ||
            AtomicInteger.class.equals(clazz) ||
            AtomicLong.class.equals(clazz) ||
            BigInteger.class.equals(clazz);
    }
    
    public static boolean isVoid(Class<?> clazz) {
        return clazz.equals(void.class) || clazz.equals(Void.class);
    }
    
    @SuppressWarnings("PMD.CyclomaticComplexity")
    public static boolean isSuper(Class<?> ssuper, Class<?> type) {
        if (ssuper.equals(boolean.class) || ssuper.equals(Boolean.class)) {
            return type.equals(boolean.class) || type.equals(Boolean.class);
        } else if (ssuper.equals(char.class) || ssuper.equals(Character.class)) {
            return type.equals(char.class) || type.equals(Character.class);
        } else if (ssuper.equals(byte.class) || ssuper.equals(Byte.class)) {
            return type.equals(byte.class) || type.equals(Byte.class);
        } else if (ssuper.equals(short.class) || ssuper.equals(Short.class)) {
            return type.equals(short.class) || type.equals(Short.class);
        } else if (ssuper.equals(int.class) || ssuper.equals(Integer.class)) {
            return type.equals(int.class) || type.equals(Integer.class);
        } else if (ssuper.equals(long.class) || ssuper.equals(Long.class)) {
            return type.equals(long.class) || type.equals(Long.class);
        } else if (ssuper.equals(float.class) || ssuper.equals(Float.class)) {
            return type.equals(float.class) || type.equals(Float.class);
        } else if (ssuper.equals(double.class) || ssuper.equals(Double.class)) {
            return type.equals(double.class) || type.equals(Double.class);
        } else {
            return ssuper.isAssignableFrom(type) || ssuper.isAssignableFrom(ClassUtils.box(type));
        }
    }
    
    public static boolean isSuper(Class<?>[] supers, Class<?>[] types) {
        if(supers.length != types.length) {
            return false;
        }
        for(int i=0; i<supers.length; i++) {
            if( !isSuper(supers[i], types[i]) ) { return false; }
        }
        return true;
    }
    
}
