package uk.jamierocks.mappingsgen;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class MappingsGen {

    // These are the input files
    private static final File classRules = new File("input/client.rules");
    private static final File classRgs = new File("input/client.rgs");
    // This is the file to output to
    private static final File classOutput = new File("output/classes.srg");

    // This map stores the deobf -> obf class names
    private static Map<String, String> deobfMappings = Maps.newHashMap();
    private static Map<String, String> obfMappings = Maps.newHashMap();

    // These Lists are used to seperate the methods and fields in the output file
    private static List<String> fieldLines = Lists.newArrayList();
    private static List<String> methodLines = Lists.newArrayList();

    public static void main(String[] args) throws IOException {
        FileInputStream rules = new FileInputStream(classRules);
        FileInputStream rgs = new FileInputStream(classRgs);
        FileOutputStream srgFile = new FileOutputStream(classOutput);
        Scanner rulesScanner = new Scanner(rules);
        Scanner rgsScanner = new Scanner(rgs);

        while (rulesScanner.hasNext()) {
            String line = rulesScanner.nextLine();
            line = line.replace("rule ", "");

            String[] mapping = line.split(" ");

            String classLine = String.format("CL: %s %s\n", mapping[0].replace(".", "/"), mapping[1].replace(".", "/"));
            byte[] data = classLine.getBytes();

            if (!classLine.contains("@")) {
                deobfMappings.put(mapping[1].replace(".", "/"), mapping[0].replace(".", "/"));
                obfMappings.put(mapping[0].replace(".", "/"), mapping[1].replace(".", "/"));
                srgFile.write(data, 0, data.length);
            }
        }

        while(rgsScanner.hasNext()) {
            String line = rgsScanner.nextLine();
            if (line.startsWith(".field_map ")) {
                line = line.replace(".field_map ", "");

                String[] mapping = line.split(" ");

                String[] thingy = mapping[0].split("/");
                String thing = thingy[thingy.length-1];

                String className = mapping[0].replace("/" + thing, "");

                String gg = deobfMappings.get(className);
                if (gg == null || gg.equals("")) {
                    gg = className;
                }

                String obfuscated = gg + "/" + thing;
                //System.out.println(className);
                //System.out.println(gg);
                //System.out.println(obfuscated);
                //System.out.println(" ");
                String deobfuscated = className + "/" + mapping[1];

                String fieldLine = String.format("FD: %s %s\n", obfuscated, deobfuscated);

                if (!fieldLine.contains("$") && !thing.equalsIgnoreCase(mapping[1])) {
                    fieldLines.add(fieldLine);
                }
            } else if(line.startsWith(".method_map ")) {
                line = line.replace(".method_map ", "");

                String[] mappings = line.split(" ");

                String original = getOriginalMapping(mappings[0]);
                String originalType = getOriginalType(mappings[1]);
                String modified = getModifiedMapping(mappings[0], mappings[2]);
                String modifiedType = mappings[1];

                String methodLine = String.format("MD: %s %s %s %s\n", original, originalType, modified, modifiedType);

                String[] originalSplit = original.split("/");
                String lastOriginal = originalSplit[originalSplit.length-1];

                if (!lastOriginal.equalsIgnoreCase(mappings[2])) {
                    methodLines.add(methodLine);
                }
            }
        }

        for  (String fieldLine : fieldLines) {
            srgFile.write(fieldLine.getBytes());
        }

        for (String methodLine : methodLines) {
            srgFile.write(methodLine.getBytes());
        }

        rules.close();
        srgFile.close();
    }

    public static String getModifiedMapping(String originalMapping, String newMapping) {
        String[] split = originalMapping.split("/");
        String lastSplit = split[split.length-1];
        String className = originalMapping.replace("/" + lastSplit, "");

        return className + "/" + newMapping;
    }

    public static String getOriginalMapping(String modifiedMapping) {
        String[] split = modifiedMapping.split("/");
        String lastSplit = split[split.length-1];
        String className = modifiedMapping.replace("/" + lastSplit, "");

        String originalClassName = className;
        if (deobfMappings.containsKey(className)) {
            originalClassName = deobfMappings.get(className);
        }

        return originalClassName + "/" + lastSplit;
    }

    public static String getOriginalType(String modifiedType) {
        String innerContent = modifiedType.substring(modifiedType.indexOf("(") + 1, modifiedType.indexOf(")"));
        String outerContent = modifiedType.substring(modifiedType.indexOf(")") + 1);

        String originalType = modifiedType;

        for (String type : innerContent.split(";")) {
            if (type.startsWith("L")) {
                String newType = type.substring(1);
                if (deobfMappings.containsKey(newType)) {
                    originalType = originalType.replace(newType, deobfMappings.get(newType));
                }
            }
        }

        if (outerContent.startsWith("L")) {
            String outerType = outerContent.substring(1, outerContent.length() - 1);
            if (deobfMappings.containsKey(outerType)) {
                originalType = originalType.replace(outerType, deobfMappings.get(outerType));
            }
        }

        return originalType;
    }
}

@@ -2278,32 +2278,33 @@
 String%5B%5D mapping
+s
  = line.split(%22 
@@ -2334,40 +2334,50 @@
 ring
-%5B%5D thingy = 
+ original = getOriginalMapping(
 mapping
+s
 %5B0%5D
-.split(%22/%22
 );%0A 
@@ -2402,111 +2402,62 @@
 ing 
-thing = thingy%5Bthingy.length-1%5D;%0A%0A                String className = 
+modified = getModifiedMapping(
 mapping
+s
 %5B0%5D
-.replace(%22/%22 + thing, %22%22
+, mappings%5B1%5D
 );%0A%0A
@@ -2483,252 +2483,71 @@
 ing 
-gg = deobfMappings.get(className);%0A                if (gg == null %7C%7C gg.equals(%22%22)) %7B%0A                    gg = className;%0A                %7D%0A%0A                String obfuscated = gg + %22/%22 + thing;%0A                //System.out.println(className
+fieldLine = String.format(%22FD: %25s %25s%5Cn%22, original, modified
 );%0A
+%0A
     
@@ -2562,123 +2562,50 @@
     
-//System.out.println(gg);%0A                //System.out.println(obfuscated);%0A                //System.out.println(%22 
+String%5B%5D originalSplit = original.split(%22/
 %22);%0A
@@ -2631,200 +2631,96 @@
 ing 
-deobfuscated = className + %22/%22 + mapping%5B1%5D;%0A%0A                String fieldLine = String.format(%22FD: %25s %25s%5Cn%22, obfuscated, deobfuscated);%0A%0A                if (!fieldLine.contains(%22$%22) && !thing
+lastOriginal = originalSplit%5BoriginalSplit.length-1%5D;%0A%0A                if (!lastOriginal
 .equ
@@ -2740,20 +2740,49 @@
 (mapping
+s
 %5B1%5D)
+ && !fieldLine.contains(%22$%22)
 ) %7B%0A    

Rewrite field code