package jadx.core.dex.instructions;

import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.InsnUtils;

import com.android.dx.io.instructions.DecodedInstruction;

import static jadx.core.utils.BlockUtils.getBlockByOffset;
import static jadx.core.utils.BlockUtils.selectOther;

public class IfNode extends GotoNode {

	private static final ArgType ARG_TYPE = ArgType.unknown(
			PrimitiveType.INT, PrimitiveType.OBJECT, PrimitiveType.ARRAY,
			PrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.CHAR);

	protected IfOp op;

	private BlockNode thenBlock;
	private BlockNode elseBlock;

	public IfNode(DecodedInstruction insn, IfOp op) {
		this(op, insn.getTarget(),
				InsnArg.reg(insn, 0, ARG_TYPE),
				insn.getRegisterCount() == 1 ? InsnArg.lit(0, ARG_TYPE) : InsnArg.reg(insn, 1, ARG_TYPE));
	}

	public IfNode(IfOp op, int targetOffset, InsnArg arg1, InsnArg arg2) {
		super(InsnType.IF, targetOffset, 2);
		this.op = op;
		addArg(arg1);
		addArg(arg2);
	}

	public IfOp getOp() {
		return op;
	}

	public void invertCondition() {
		op = op.invert();
		BlockNode tmp = thenBlock;
		thenBlock = elseBlock;
		elseBlock = tmp;
		target = thenBlock.getStartOffset();
	}

	public void changeCondition(IfOp op, InsnArg arg1, InsnArg arg2) {
		this.op = op;
		setArg(0, arg1);
		setArg(1, arg2);
	}

	public void initBlocks(BlockNode curBlock) {
		thenBlock = getBlockByOffset(target, curBlock.getSuccessors());
		if (curBlock.getSuccessors().size() == 1) {
			elseBlock = thenBlock;
		} else {
			elseBlock = selectOther(thenBlock, curBlock.getSuccessors());
		}
	}

	public BlockNode getThenBlock() {
		return thenBlock;
	}

	public BlockNode getElseBlock() {
		return elseBlock;
	}

	@Override
	public boolean isSame(InsnNode obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof IfNode) || !super.isSame(obj)) {
			return false;
		}
		IfNode other = (IfNode) obj;
		return op == other.op;
	}

	@Override
	public String toString() {
		return InsnUtils.formatOffset(offset) + ": "
				+ InsnUtils.insnTypeToString(insnType)
				+ getArg(0) + " " + op.getSymbol() + " " + getArg(1)
				+ "  -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target));
	}
}
