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

import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.Set;
import net.ranides.assira.collection.sets.Bitmask;
import net.ranides.assira.reflection.util.ClassTraits;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public enum IAttribute {
    
    PUBLIC          (0x00000001),

    PRIVATE         (0x00000002),

    PROTECTED       (0x00000004),

    STATIC          (0x00000008),

    FINAL           (0x00000010),

    SYNCHRONIZED    (0x00000020),

    VOLATILE        (0x00000040),   // special: must be field (conflict with BRIDGE)

    TRANSIENT       (0x00000080),   // special: must be field (conflict with VARARGS)

    NATIVE          (0x00000100),

    INTERFACE       (0x00000200),

    ABSTRACT        (0x00000400),

    STRICT          (0x00000800),

    BRIDGE          (0x01000000),   // special: must be method (conflict with VOLATILE)
    
    VARARGS         (0x02000000),   // special: must be method (conflict with TRANSIENT)
    
    SYNTHETIC       (0x00001000),
    
    ANNOTATION      (0x00002000),
    
    ENUM            (0x00004000),
    
    MANDATED        (0x00008000),

    PACKAGE         (0x04000000),   // special: !(public | protected | private)
    
    PARAMETERIZED   (0x10000000),   // special: is generic class
    
    DECLARED        (0x40000000),   // special: used in IClass to disable deep scan
    
    LAMBDA          (0x80000000),   // special: used in IMethod
    
    ANY             (0x00200000),   // special: used only by AHints for deep scan of declared fields/methods/classes

    ARRAY           (0x00010000),

    PRIMITIVE       (0x00020000), 
    
    NUMBER          (0x20000000),
    
    INTEGER         (0x00400000),

    BOXED           (0x00040000),
    
    VOID            (0x00080000),
    
    BOOLEAN         (0x00100000),
    
    //RESERVED        (0x08000000),
    
    //RESERVED        (0x00800000),
    
    ;
    
    private static final int M_BRIDGE   = 0x00000040;
    
    private static final int M_VARARGS  = 0x00000080;
    
    private static final int ACCESS_ANY = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
    
    private static final Bitmask<IAttribute> MAP = new Bitmask<>(IAttribute.values(), a -> a.mask);
    
    private final int mask;

    private IAttribute(int mask) {
        this.mask = mask;
    }
    
    public static Set<IAttribute> clone(Set<IAttribute> attrs) {
        return MAP.clone(attrs);
    }
    
    public static Set<IAttribute> constant(IAttribute... attrs) {
        return MAP.constant(attrs);
    }
    
    public static Set<IAttribute> collect(IAttribute... attrs) {
        return MAP.collect(attrs);
    }
    
    public static Set<IAttribute> resolve(Field field) {
        int mask = getModifiers(field.getModifiers());
        if(!(field.getGenericType() instanceof Class)) {
            mask |= PARAMETERIZED.mask;
        }
        return MAP.collect(mask);
    }
    
    public static Set<IAttribute> resolve(Executable method) {
        int mask = getModifiers(method.getModifiers());
        if(method instanceof Method) {
            mask &= ~DECLARED.mask;
        }
        if( 0 != ((M_BRIDGE|M_VARARGS) & mask) ) {
            mask |= (0 != (mask & M_BRIDGE)) ? BRIDGE.mask : 0;
            mask |= (0 != (mask & M_VARARGS)) ? VARARGS.mask : 0;
            mask &= ~(M_BRIDGE|M_VARARGS);
        }
        if( 0 != method.getTypeParameters().length ) {
            mask |= PARAMETERIZED.mask;
        }
        return MAP.collect(mask);
    }
    
    public static Set<IAttribute> resolve(Parameter param) {
        int mask = getModifiers(param.getModifiers());
        if(!(param.getParameterizedType() instanceof Class)) {
            mask |= PARAMETERIZED.mask;
        }
        return MAP.collect(mask);
    }
    
    public static Set<IAttribute> resolve(Class<?> type) {
        int mask = getModifiers(type.getModifiers());
        if( 0 != type.getTypeParameters().length) {
            mask |= PARAMETERIZED.mask;
        }
        if( ClassTraits.isBoxed(type)) {
            mask |= BOXED.mask;
        }
        if( ClassTraits.isPrimitive(type)) {
            mask |= PRIMITIVE.mask;
        }
        if( ClassTraits.isNumber(type)) {
            mask |= NUMBER.mask;
        }
        if( ClassTraits.isInteger(type)) {
            mask |= INTEGER.mask;
        }
        if(type.isArray()) {
            mask |= ARRAY.mask;
        }
        if(ClassTraits.isVoid(type)) {
            mask |= VOID.mask;
        }
        if(ClassTraits.isBool(type)) {
            mask |= BOOLEAN.mask;
        }
        return MAP.collect(mask);
    }
    
    private static int getModifiers(int mask) {
        mask |= DECLARED.mask;
        if( 0 == (mask & ACCESS_ANY)) {
            mask |= PACKAGE.mask;
        }
        return mask;
    }
    
    public boolean matches(IElement element) {
        return element!=null && element.attributes().contains(this);
    }
    
}
