package alterrs.jbct.ref;

import java.util.Set;

import org.slf4j.Logger;

import alterrs.asm.tree.ClassNode;
import alterrs.asm.tree.FieldNode;
import alterrs.commons.LoggingUtil;
import alterrs.jbct.JBCT;
import alterrs.jbct.utility.AbstractTransformer;
import alterrs.jbct.utility.ClassCollection;
import alterrs.jbct.utility.FieldID;
import alterrs.jbct.utility.ID;
import alterrs.jbct.utility.IdentifierUtil;

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

	private int fieldId = 1;

	public FieldNameDeobfuscator() {
		super(true);
	}

	@Override
	public void transform(ClassCollection cc) {
		for (int level = 0; level < 32; level++) {
			for (ClassNode c : cc.getClasses().values()) {
				if (cc.getLevel(c) == level) {
					fieldLoop: for (Object fo : c.fields) {
						FieldNode f = (FieldNode) fo;

						if (f.name.length() <= 3
								&& JBCT.ctx.module.getRefactorer().getField(
										new FieldID(c, f)) == null) {
							boolean illegal = IdentifierUtil
									.illegalIdentifier(f.name);
							boolean force = false;
							if (JBCT.ctx.atr.<Set<ID>> get("ignoredClasses")
									.contains(new ID(c.name))) {
								if (!illegal)
									continue fieldLoop;
								force = true;
							}

							String intro;
							String type;
							String array = "";
							String simpleDesc = f.desc.replace("[", "");

							if (simpleDesc.equals("Z")) {
								type = "Bool";
							} else if (simpleDesc.equals("B")) {
								type = "Byte";
							} else if (simpleDesc.equals("C")) {
								type = "Char";
							} else if (simpleDesc.equals("S")) {
								type = "Short";
							} else if (simpleDesc.equals("I")) {
								type = "Int";
							} else if (simpleDesc.equals("J")) {
								type = "Long";
							} else if (simpleDesc.equals("F")) {
								type = "Float";
							} else if (simpleDesc.equals("D")) {
								type = "Double";
							} else if (simpleDesc.startsWith("L")) {
								type = simpleDesc.substring(1,
										simpleDesc.length() - 1);

								if (JBCT.ctx.module.getRefactorer().getClass(
										new ID(type)) != null) {
									type = JBCT.ctx.module.getRefactorer()
											.getClass(new ID(type));
								}

								if (type.contains("/")) {
									type = type
											.substring(type.lastIndexOf("/") + 1);
								}

							} else {
								return;
							}

							int dimensions = 1;
							for (char ch : f.desc.toCharArray()) {
								if (ch == '[') {
									dimensions++;
								}
							}

							for (int i = 0; i < dimensions - 1; i++) {
								array += "Array";
							}

							if (startsWithVowel(type)) {
								intro = "an";
							} else {
								intro = "a";
							}

							String name = intro
									+ type
									+ array
									+ ((dimensions == 1 && endsWithNumber(type)) ? "_"
											: "") + fieldId++;
							JBCT.ctx.module.getRefactorer().refactorField(
									new FieldID(c, f), name);

							if (force) {
								logger.warn("Forced to rename field : "
										+ new FieldID(c, f));
							}
						}
					}
				}
			}
		}
	}

	private static char[] VOWELS = { 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I',
			'O', 'U' };

	private static boolean startsWithVowel(String string) {
		char[] stringChars = string.toCharArray();
		vowelLoop: for (char c : VOWELS) {
			for (char c2 : stringChars) {
				if (Character.isLetter(c2)) {
					if (c2 == c) {
						return true;
					} else {
						continue vowelLoop;
					}
				}
			}
		}
		return false;
	}

	private static boolean endsWithNumber(String string) {
		return Character.isDigit(string.toCharArray()[string.length() - 1]);
	}

	@Override
	public void onFinish() {
		logger.info("Refactored " + (fieldId - 1) + " fields!");
	}
}
