package jadx.core.dex.info;

import com.android.dx.rop.code.AccessFlags;

import jadx.core.Consts;

public class AccessInfo {

	public static final int VISIBILITY_FLAGS = AccessFlags.ACC_PUBLIC | AccessFlags.ACC_PROTECTED | AccessFlags.ACC_PRIVATE;
	private final int accFlags;

	public enum AFType {
		CLASS, FIELD, METHOD
	}

	private final AFType type;

	public AccessInfo(int accessFlags, AFType type) {
		this.accFlags = accessFlags;
		this.type = type;
	}

	public boolean containsFlag(int flag) {
		return (accFlags & flag) != 0;
	}

	public AccessInfo remove(int flag) {
		if (containsFlag(flag)) {
			return new AccessInfo(accFlags & ~flag, type);
		}
		return this;
	}

	public AccessInfo add(int flag) {
		if (!containsFlag(flag)) {
			return new AccessInfo(accFlags | flag, type);
		}
		return this;
	}

	public AccessInfo changeVisibility(int flag) {
		int currentVisFlags = accFlags & VISIBILITY_FLAGS;
		if (currentVisFlags == flag) {
			return this;
		}
		int unsetAllVisFlags = accFlags & ~VISIBILITY_FLAGS;
		return new AccessInfo(unsetAllVisFlags | flag, type);
	}

	public AccessInfo getVisibility() {
		return new AccessInfo(accFlags & VISIBILITY_FLAGS, type);
	}

	public boolean isPublic() {
		return (accFlags & AccessFlags.ACC_PUBLIC) != 0;
	}

	public boolean isProtected() {
		return (accFlags & AccessFlags.ACC_PROTECTED) != 0;
	}

	public boolean isPrivate() {
		return (accFlags & AccessFlags.ACC_PRIVATE) != 0;
	}

	public boolean isAbstract() {
		return (accFlags & AccessFlags.ACC_ABSTRACT) != 0;
	}

	public boolean isInterface() {
		return (accFlags & AccessFlags.ACC_INTERFACE) != 0;
	}

	public boolean isAnnotation() {
		return (accFlags & AccessFlags.ACC_ANNOTATION) != 0;
	}

	public boolean isNative() {
		return (accFlags & AccessFlags.ACC_NATIVE) != 0;
	}

	public boolean isStatic() {
		return (accFlags & AccessFlags.ACC_STATIC) != 0;
	}

	public boolean isFinal() {
		return (accFlags & AccessFlags.ACC_FINAL) != 0;
	}

	public boolean isConstructor() {
		return (accFlags & AccessFlags.ACC_CONSTRUCTOR) != 0;
	}

	public boolean isEnum() {
		return (accFlags & AccessFlags.ACC_ENUM) != 0;
	}

	public boolean isSynthetic() {
		return (accFlags & AccessFlags.ACC_SYNTHETIC) != 0;
	}

	public boolean isBridge() {
		return (accFlags & AccessFlags.ACC_BRIDGE) != 0;
	}

	public boolean isVarArgs() {
		return (accFlags & AccessFlags.ACC_VARARGS) != 0;
	}

	public boolean isSynchronized() {
		return (accFlags & (AccessFlags.ACC_SYNCHRONIZED | AccessFlags.ACC_DECLARED_SYNCHRONIZED)) != 0;
	}

	public boolean isTransient() {
		return (accFlags & AccessFlags.ACC_TRANSIENT) != 0;
	}

	public boolean isVolatile() {
		return (accFlags & AccessFlags.ACC_VOLATILE) != 0;
	}

	public AFType getType() {
		return type;
	}

	public String makeString() {
		StringBuilder code = new StringBuilder();
		if (isPublic()) {
			code.append("public ");
		}
		if (isPrivate()) {
			code.append("private ");
		}
		if (isProtected()) {
			code.append("protected ");
		}
		if (isStatic()) {
			code.append("static ");
		}
		if (isFinal()) {
			code.append("final ");
		}
		if (isAbstract()) {
			code.append("abstract ");
		}
		if (isNative()) {
			code.append("native ");
		}
		switch (type) {
			case METHOD:
				if (isSynchronized()) {
					code.append("synchronized ");
				}
				if (isBridge()) {
					code.append("/* bridge */ ");
				}
				if (Consts.DEBUG && isVarArgs()) {
					code.append("/* varargs */ ");
				}
				break;

			case FIELD:
				if (isVolatile()) {
					code.append("volatile ");
				}
				if (isTransient()) {
					code.append("transient ");
				}
				break;

			case CLASS:
				if ((accFlags & AccessFlags.ACC_STRICT) != 0) {
					code.append("strict ");
				}
				if (Consts.DEBUG) {
					if ((accFlags & AccessFlags.ACC_SUPER) != 0) {
						code.append("/* super */ ");
					}
					if ((accFlags & AccessFlags.ACC_ENUM) != 0) {
						code.append("/* enum */ ");
					}
				}
				break;
		}
		if (isSynthetic()) {
			code.append("/* synthetic */ ");
		}
		return code.toString();
	}

	public String rawString() {
		switch (type) {
			case CLASS:
				return AccessFlags.classString(accFlags);
			case FIELD:
				return AccessFlags.fieldString(accFlags);
			case METHOD:
				return AccessFlags.methodString(accFlags);
			default:
				return "?";
		}
	}

	public int rawValue() {
		return accFlags;
	}

	@Override
	public String toString() {
		return "AccessInfo: " + type + " 0x" + Integer.toHexString(accFlags) + " (" + rawString() + ')';
	}
}
