package alterrs.jbct.deob;

import static alterrs.asm.Opcodes.ACC_FINAL;
import static alterrs.asm.Opcodes.INVOKESPECIAL;
import static alterrs.asm.Opcodes.PUTFIELD;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;

import alterrs.asm.tree.AbstractInsnNode;
import alterrs.asm.tree.ClassNode;
import alterrs.asm.tree.FieldInsnNode;
import alterrs.asm.tree.FieldNode;
import alterrs.asm.tree.InsnList;
import alterrs.asm.tree.MethodNode;
import alterrs.commons.LoggingUtil;
import alterrs.jbct.JBCT;
import alterrs.jbct.utility.AbstractTransformer;
import alterrs.jbct.utility.FieldID;

/**
 * @author Lazaro Brito
 */
public class FieldDeobfuscator extends AbstractTransformer {
	private static final Logger logger = LoggingUtil.log();

	private int count = 0, count2 = 0;

	public FieldDeobfuscator() {
		super(true);
	}

	@Override
	public void visitMethod(ClassNode c, MethodNode m) {
		if (m.name.equals("<init>")) {
			AbstractInsnNode n = m.instructions.getFirst();
			while (n != null) {
				if (n.getOpcode() == PUTFIELD) {
					List<AbstractInsnNode> block = new ArrayList<AbstractInsnNode>();

					AbstractInsnNode n2 = m.instructions.getFirst();
					while (n2 != null) {
						block.add(n2);

						if (n2 == n) {
							break;
						}
						n2 = n2.getNext();
					}

					for (AbstractInsnNode n3 : block) {
						m.instructions.remove(n3);
					}

					n2 = m.instructions.getFirst();
					while (n2 != null && n2.getOpcode() != INVOKESPECIAL) {
						n2 = n2.getNext();
					}

					InsnList list = new InsnList();
					for (AbstractInsnNode n3 : block) {
						list.add(n3);
					}

					m.instructions.insert(n2, list);
					count2++;
					break;
				} else if (n.getOpcode() == INVOKESPECIAL) {
					return;
				}

				n = n.getNext();
			}
		}
	}

	@Override
	public void visitFieldInsn(ClassNode c, MethodNode m, FieldInsnNode n) {
		FieldNode f = JBCT.ctx.module.field(new FieldID(n));
		if (f != null && (f.access & ACC_FINAL) != 0) {
			f.access &= ~ACC_FINAL;

			count++;
		}
	}

	@Override
	public void onFinish() {
		logger.info("Definalized " + count + " fields!");
		logger.info("Reorganized " + count2 + " put-fields!");
	}
}
