package com.patchworkmc.patch;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import com.patchworkmc.Patchwork;
import com.patchworkmc.mapping.remapper.AmbiguousMappingException;
import com.patchworkmc.mapping.remapper.PatchworkRemapper;

/**
 * Remaps Strings into Intermediary. Necessary for things like reflection and ObfuscationRemapperHelper,
 * unless we put SRG and tiny-remapper on the classpath at runtime.
 */
public class StringConstantRemapper extends ClassVisitor {
	private PatchworkRemapper.Naive remapper;

	public StringConstantRemapper(ClassVisitor classVisitor, PatchworkRemapper.Naive remapper) {
		super(Opcodes.ASM7, classVisitor);
		this.remapper = remapper;
	}

	/**
	 * Remaps strings in fields.
	 */
	@Override
	public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
		if (value instanceof String) {
			value = remap((String) value);
		}

		return super.visitField(access, name, descriptor, signature, value);
	}

	@Override
	public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
		return new MethodTransformer(super.visitMethod(access, name, descriptor, signature, exceptions));
	}

	private String remap(String name) {
		// if unable to remap these methods return the names they received.
		if (name.startsWith("field_")) {
			name = remapper.getField(name);
		} else if (name.startsWith("func_")) {
			try {
				name = remapper.getMethod(name);
			} catch (AmbiguousMappingException e) {
				Patchwork.LOGGER.warn("Failed to remap string constant: %s", e.getMessage());

				return name;
			}
		} else {
			name = remapper.getClass(name);
		}

		return name;
	}

	/**
	 * Remaps LDC calls (string construction) in method bytecode.
	 */
	private class MethodTransformer extends MethodVisitor {
		private MethodTransformer(MethodVisitor methodVisitor) {
			super(Opcodes.ASM7, methodVisitor);
		}

		@Override
		public void visitLdcInsn(Object value) {
			if (value instanceof String) {
				value = remap((String) value);
			}

			super.visitLdcInsn(value);
		}
	}
}