package jadx.core.dex.info;

import jadx.core.codegen.TypeGen;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.DexNode;
import jadx.core.utils.Utils;

import java.util.List;

import com.android.dex.MethodId;
import com.android.dex.ProtoId;

public final class MethodInfo {

	private final String name;
	private final ArgType retType;
	private final List<ArgType> args;
	private final ClassInfo declClass;
	private final String shortId;

	private MethodInfo(DexNode dex, int mthIndex) {
		MethodId mthId = dex.getMethodId(mthIndex);
		name = dex.getString(mthId.getNameIndex());
		declClass = ClassInfo.fromDex(dex, mthId.getDeclaringClassIndex());

		ProtoId proto = dex.getProtoId(mthId.getProtoIndex());
		retType = dex.getType(proto.getReturnTypeIndex());
		args = dex.readParamList(proto.getParametersOffset());

		StringBuilder signature = new StringBuilder();
		signature.append(name);
		signature.append('(');
		for (ArgType arg : args) {
			signature.append(TypeGen.signature(arg));
		}
		signature.append(')');
		signature.append(TypeGen.signature(retType));

		shortId = signature.toString();
	}

	public static MethodInfo fromDex(DexNode dex, int mthIndex) {
		MethodInfo mth = dex.getInfoStorage().getMethod(mthIndex);
		if (mth != null) {
			return mth;
		}
		mth = new MethodInfo(dex, mthIndex);
		return dex.getInfoStorage().putMethod(mthIndex, mth);
	}

	public String getName() {
		return name;
	}

	public String getFullName() {
		return declClass.getFullName() + "." + name;
	}

	public String getFullId() {
		return declClass.getFullName() + "." + shortId;
	}

	/**
	 * Method name and signature
	 */
	public String getShortId() {
		return shortId;
	}

	public ClassInfo getDeclClass() {
		return declClass;
	}

	public ArgType getReturnType() {
		return retType;
	}

	public List<ArgType> getArgumentsTypes() {
		return args;
	}

	public int getArgsCount() {
		return args.size();
	}

	public boolean isConstructor() {
		return name.equals("<init>");
	}

	public boolean isClassInit() {
		return name.equals("<clinit>");
	}

	@Override
	public int hashCode() {
		int result = declClass.hashCode();
		result = 31 * result + retType.hashCode();
		result = 31 * result + shortId.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		MethodInfo other = (MethodInfo) obj;
		return shortId.equals(other.shortId)
				&& retType.equals(other.retType)
				&& declClass.equals(other.declClass);
	}

	@Override
	public String toString() {
		return declClass.getFullName() + "." + name
				+ "(" + Utils.listToString(args) + "):" + retType;
	}

}
