package jadx.core.dex.info;

import jadx.core.Consts;
import jadx.core.deobf.NameMapper;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.DexNode;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.io.File;

public final class ClassInfo {

	private final ArgType type;
	private String pkg;
	private String name;
	private String fullName;
	// for inner class not equals null
	private ClassInfo parentClass;
	// class info after rename (deobfuscation)
	private ClassInfo alias;

	private ClassInfo(DexNode dex, ArgType type) {
		this(dex, type, true);
	}

	private ClassInfo(DexNode dex, ArgType type, boolean inner) {
		if (!type.isObject() || type.isGeneric()) {
			throw new JadxRuntimeException("Not class type: " + type);
		}
		this.type = type;
		this.alias = this;

		splitNames(dex, inner);
	}

	public static ClassInfo fromType(DexNode dex, ArgType type) {
		if (type.isArray()) {
			type = ArgType.OBJECT;
		}
		ClassInfo cls = dex.getInfoStorage().getCls(type);
		if (cls != null) {
			return cls;
		}
		cls = new ClassInfo(dex, type);
		return dex.getInfoStorage().putCls(cls);
	}

	public static ClassInfo fromDex(DexNode dex, int clsIndex) {
		if (clsIndex == DexNode.NO_INDEX) {
			return null;
		}
		return fromType(dex, dex.getType(clsIndex));
	}

	public static ClassInfo fromName(DexNode dex, String clsName) {
		return fromType(dex, ArgType.object(clsName));
	}

	public static ClassInfo extCls(DexNode dex, ArgType type) {
		ClassInfo classInfo = fromName(dex, type.getObject());
		return classInfo.alias;
	}

	public void rename(DexNode dex, String fullName) {
		ClassInfo newAlias = new ClassInfo(dex, ArgType.object(fullName), isInner());
		if (!alias.getFullName().equals(newAlias.getFullName())) {
			this.alias = newAlias;
		}
	}

	public boolean isRenamed() {
		return alias != this;
	}

	public ClassInfo getAlias() {
		return alias;
	}

	private void splitNames(DexNode dex, boolean canBeInner) {
		String fullObjectName = type.getObject();
		String clsName;
		int dot = fullObjectName.lastIndexOf('.');
		if (dot == -1) {
			pkg = "";
			clsName = fullObjectName;
		} else {
			pkg = fullObjectName.substring(0, dot);
			clsName = fullObjectName.substring(dot + 1);
		}

		int sep = clsName.lastIndexOf('$');
		if (canBeInner && sep > 0 && sep != clsName.length() - 1) {
			String parClsName = pkg + "." + clsName.substring(0, sep);
			parentClass = fromName(dex, parClsName);
			clsName = clsName.substring(sep + 1);
		} else {
			parentClass = null;
		}

		char firstChar = clsName.charAt(0);
		if (Character.isDigit(firstChar)) {
			clsName = Consts.ANONYMOUS_CLASS_PREFIX + clsName;
		} else if (firstChar == '$') {
			clsName = "_" + clsName;
		}
		if (NameMapper.isReserved(clsName)) {
			clsName += "_";
		}
		this.name = clsName;
		if (parentClass != null) {
			this.fullName = parentClass.fullName + "." + clsName;
		} else {
			this.fullName = pkg.isEmpty() ? clsName : pkg + "." + clsName;
		}
	}

	public String getFullPath() {
		return pkg.replace('.', File.separatorChar)
				+ File.separatorChar
				+ getNameWithoutPackage().replace('.', '_');
	}

	public String getFullName() {
		return fullName;
	}

	public String getShortName() {
		return name;
	}

	public String getPackage() {
		return pkg;
	}

	public String getRawName() {
		return type.getObject();
	}

	public String getNameWithoutPackage() {
		if (parentClass == null) {
			return name;
		}
		return parentClass.getNameWithoutPackage() + "." + name;
	}

	public ClassInfo getParentClass() {
		return parentClass;
	}

	public boolean isInner() {
		return parentClass != null;
	}

	public void notInner(DexNode dex) {
		splitNames(dex, false);
	}

	public ArgType getType() {
		return type;
	}

	@Override
	public String toString() {
		return fullName;
	}

	@Override
	public int hashCode() {
		return fullName.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof ClassInfo) {
			ClassInfo other = (ClassInfo) obj;
			return this.type.equals(other.type);
		}
		return false;
	}
}
