/*
    JPC: A x86 PC Hardware Emulator for a pure Java Virtual Machine
    Release Version 2.0

    A project from the Physics Dept, The University of Oxford

    Copyright (C) 2007 Isis Innovation Limited

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
    Details (including contact information) can be found at: 

    www.physics.ox.ac.uk/jpc
*/

import java.io.*;
import java.util.regex.*;

//to use this run it in the sme directory as RealModeUBlock.java and MicrocodeSet.java

public class convert
{
    static String codestrings = ""; 
    static BufferedWriter result;
    static String callArguments ="";
    static String methodArguments ="";
    static boolean callsMethod = false;
    static String name = "";
    static String methodCode = "";
    static int count = 0, count2 = 0, count3 = 0, count4 = 0, count5 = 0;
    
    
    //class to read in method names from output find the corresponding method code in in
    //and output the results in result
    public static void main(String[] args)
    {
        try 
        {
            BufferedReader in = new BufferedReader(new FileReader("../../optimised/RealModeUBlock.java"));
            //BufferedReader in = new BufferedReader(new FileReader("../../optimised/ProtectedModeUBlock.java"));
            result = new BufferedWriter(new FileWriter("../RealModeUBlockSource.java"));
            //result = new BufferedWriter(new FileWriter("../ProtectedModeUBlockSource.java"));
            BufferedReader output = new BufferedReader(new FileReader("../../optimised/MicrocodeSet.java"));
            String str;
            
            while ((str = in.readLine()) != null) 
            {
                codestrings = codestrings + "\n" + str;
            }
            
            
            result.write("package org.jpc.emulator.memory.codeblock.compiler;\n public interface RealModeUBlockSource { \n");
            while ((str = output.readLine()) != null) 
            {
                
                String patternStr = "public static final int (.*) = ";
                Pattern pattern = Pattern.compile(patternStr);
                
                // Determine if pattern exists in input
                Matcher matcher = pattern.matcher(str);
                boolean matchFound = matcher.find();    
                
                if (matchFound)
                {
                    count++;
                    // found a microcode name from list in microcodeSet
                    name = matcher.group(1);         
                    
                    int start = codestrings.indexOf(" " + name + ":");
                    if (start == -1)
                        continue;
                    start = codestrings.indexOf(":", start);
                    if (start == -1)
                        continue;
                    int end = codestrings.indexOf("break;", start);
                    if (end == -1)
                        continue;
                    // found matching code for method name in case statement
                    String code = codestrings.substring(start + 1, end);
                    count2++;

                    //check if matching string calls a method anywhere
                    methodCode = fetchMethod(code);

                    //now we need to search the method code and see if it calls another method, if so, go and get that code too
                    methodCode = fetchMethod(methodCode); 
                    methodCode = fetchMethod(methodCode);  
                    
                    //remove all end of line characters to allow java to parse it as a string
                    methodCode = methodCode.replace("\n", "");
                    methodCode = methodCode.replace("\r", "");
                    methodCode = methodCode.replace("\u0085", "");
                    methodCode = methodCode.replace("\"", "\\\"");
                    methodCode = methodCode.replace("io(reg0)s", "ioports"); //fudge to fix variable replacement
                    //methodCode = methodCode.replace("if ((cpu.eip & 0xFFFF0000) == 0)      return;", "");

                    if (methodCode.compareTo("null") != 0)
                        methodCode = "\"{" + methodCode + "}\"";
                    
                    //write result to file
                    if (name.compareTo("EIP_UPDATE") ==0)
                    {
                        result.write("static final String " + name + " = \"{ if (!eipUpdated) {eipUpdated = true; cpu.eip += getX86Length(); } }\"  ;\n");
                    }
                    else
                    {
                        result.write("static final String " + name + " = " + methodCode + ";\n");
                    }
                    result.flush();
                    callsMethod = false;
                }
            }
            System.out.println(count + " microcodes");
            System.out.println(count2 + " implemented microcodes");
            System.out.println(count3 + " direct method calls");
            System.out.println(count4 + " method calls with no arguments");
            result.write("}");
            result.close();
            output.close();
            in.close();
        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }
    }
    
    public static String fetchMethod(String haystack)
    {
        //check if matching string is calling a method and if so go get the source for that method
        String methodPattern = "[^\\.\\w]([_\\w]+)\\(([\\(\\) ,&-_\\w]*)\\);";
        Pattern methodPattern1 = Pattern.compile(methodPattern, Pattern.MULTILINE);
        Matcher methodMatcher = methodPattern1.matcher(haystack);
        boolean isMethod = methodMatcher.find();

        //        if (name.compareTo("RDTSC")==0)
        //    System.out.println("haystack is " + haystack);

        if (isMethod)
        {
            if (!callsMethod)
                count3++;
            //we have a method call
            String method = methodMatcher.group(1);
            String start = haystack.substring(0, haystack.indexOf(method));
            callArguments = methodMatcher.group(2);
            String end = haystack.substring(haystack.indexOf(method) + method.length() + 1 + callArguments.length() + 3, haystack.length());
            
            String codeForMethod = "private final [\\w]* " + method + "(\\([, \\w]*\\))[\\w\\s]*\\{(.*?)^\\s\\s\\s\\s\\}";
            Pattern codeInMethod = Pattern.compile(codeForMethod, Pattern.MULTILINE | Pattern.DOTALL);
            Matcher codeMatcher = codeInMethod.matcher(codestrings);
            if (codeMatcher.find())
            {
                //we have a method call, find it's code
                callsMethod  =true;

                //if (name.compareTo("RDTSC")==0)
                //    System.out.println("callargs is " + callArguments);

                methodCode = codeMatcher.group(2);
                methodArguments = codeMatcher.group(1);
                
                //need to handle commented lines and change them to /* */
                String acommentPattern = "(//.*)$";
                Pattern commentPattern = Pattern.compile(acommentPattern, Pattern.MULTILINE);
                Matcher commentMatcher = commentPattern.matcher(methodCode);
                String buf = "";
                String comment = "";
                int indexOfComment;
                while (commentMatcher.find())
                {
                    comment = commentMatcher.group(1);
                    indexOfComment = methodCode.indexOf(comment);
                    buf = methodCode.substring(0,indexOfComment) + "/*" 
                        + comment.substring(2,comment.length()) + "*/"
                        + methodCode.substring(indexOfComment + comment.length() + 1, methodCode.length());
                    methodCode = buf;
                }
                
                methodCode = methodCode.replace("\\", "\\\\");
                methodCode = start + "{" + methodCode + "}" + end;

                //replace arguments with their names in the method call
                if (callArguments.compareTo("") == 0)
                {
                    count4++;
                    return methodCode;
                }
                else
                {
                    //we have a method with arguments

                    //count the arguments
                    int numberOfArgs = 1;
                    int index = callArguments.indexOf(",", 0);
                    while (index != -1)
                    {
                        index = callArguments.indexOf(",", index + 1);
                        numberOfArgs++;
                    }

                    //substitute in the names of variables from the method call
                    for (int i=0; i < numberOfArgs; i++)
                    {
                        int startIndex = 0;
                        for (int j = 0; j < i; j++)
                        {
                            startIndex = callArguments.indexOf(",", startIndex) + 1;
                        }
                        int endIndex = callArguments.indexOf(",", startIndex);
                        if (endIndex ==-1)
                            endIndex = callArguments.length();
                        
                        String callVarName = "(" + callArguments.substring(startIndex, endIndex) + ")";  //added ()'s to cover calls like (data1-data2)
                        
                        startIndex = 0;
                        for (int j = 0; j < i; j++)
                        {
                            startIndex = methodArguments.indexOf(",", startIndex) + 1;
                        }
                        endIndex = methodArguments.indexOf(",", startIndex);
                        if (endIndex == -1)
                            endIndex = methodArguments.length()-1;
                        startIndex = methodArguments.substring(startIndex, endIndex).lastIndexOf(" ") + 1 + startIndex;

                        String methodVarName = methodArguments.substring(startIndex, endIndex);
                        methodCode = methodCode.replaceAll(methodVarName, callVarName);
                    }
                }
                return methodCode;
            }
        }
        return haystack;
    }
} 
