// ----------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ----------------------------------------------------------------------------

using System;
using System.Collections;

public class SyscallBuilder
{
    static Hashtable classTable = new Hashtable();
    static Hashtable structTable = new Hashtable();
    static Hashtable paramTable = new Hashtable();

    static int lineNumber = 0;

    static void AddType(string t)
    {
        if (t.StartsWith("struct ")) {
            int end = t.IndexOf(" ", 7);
            if (end == -1) {
                end = t.Length;
            }
            string structName = t.Substring(7, end - 7);
            if (end < t.Length) {
                structTable[structName] = "";
            }
            else {
                int ssize;
                if (structName.EndsWith("Handle")) {
                    ssize = 4;
                }
                else {
                    if (!paramTable.ContainsKey("struct " + structName)) {
                        throw new Exception("unknown struct type: " + t);
                    }
                    ssize = (int) paramTable["struct " + structName];
                }
                structTable[structName] = "char data[" + ssize + "];";
            }
        }
    }

    public static void Generate(string[] args)
    {
        bool genHdr = args[0] == "/genhdr";
        bool genLib = args[0] == "/genlib";
        bool genEntry = args[0] == "/genentry";
        if (genLib) {
            Console.WriteLine("#include \"syscalls.h\"");
            Console.WriteLine();
        }
        if (genEntry) {
            Console.WriteLine("#include \"syscalls.h\"");
        }
        int n = 1;
        paramTable["void"] = 0;
        paramTable["int"] = 4;
        paramTable["unsigned int"] = 4;
        paramTable["unsigned char"] = 4;
        paramTable["unsigned short"] = 4;
        paramTable["wchar_t"] = 4;
        paramTable["bool"] = 4;
        paramTable["__int64"] = 8;
        paramTable["unsigned __int64"] = 8;
        paramTable["struct Struct_System_TimeSpan"] = 8;
        paramTable["struct Struct_System_DateTime"] = 8;
        paramTable["struct Struct_System_SchedulerTime"] = 8;
        paramTable["struct Struct_Microsoft_Singularity_V1_Types_SystemType"] = 4;
        paramTable["struct Struct_Microsoft_Singularity_V1_Threads_ThreadState"] = 4;
        paramTable["struct Struct_Microsoft_Singularity_V1_Processes_ProcessState"] = 4;
        paramTable["struct Struct_Microsoft_Singularity_V1_Services_ParameterCode"] = 4;

        while (true) {
            string s = Console.ReadLine();
            lineNumber++;
            if (s == null) {
                break;
            }
            if (s.IndexOf('?') == -1) {
                continue;
            }
            if (   s.IndexOf("Struct_Microsoft_Singularity_V1_Services_SharedHeapService::g_GetData") != -1
                || s.IndexOf("Struct_Microsoft_Singularity_V1_Services_SharedHeapService::g_GetSize") != -1
                || s.IndexOf("Struct_Microsoft_Singularity_V1_Types_SystemType::g_IsNull") != -1) {
                continue;
            }

            if (s.IndexOf("(public: ") == -1) {     // Skip non attributed exports
                continue;
            }
            
            s = s.Replace("struct void", "void");
            int i = s.IndexOf("static");
            if (i == -1) {
                throw new Exception("not static");
            }
            i += 7;
            string decl = s.Substring(i, s.IndexOf(')', i) + 1 - i);
            i = s.IndexOf("__fastcall", i);
            if (i == -1) {
                throw new Exception("not fastcall");
            }
            string ret = decl.Substring(0, decl.IndexOf(" __fastcall"));

            try {
                AddType(ret);
            }
            catch {
                Console.WriteLine("Failed at input line {0}\n", lineNumber);
                throw;
            }

            i = s.IndexOf(' ', i) + 1;
            bool isAbi;

            {
                int j = s.IndexOf("::", i);
                int k = s.IndexOf("(", j);
                string className = s.Substring(i, j - i);
                string methodName = s.Substring(i, k - i);
                string methodLocalName = s.Substring(j + 2, k - (j + 2));

                // These two symbols are special and shouldn't have ring3
                // stubs
                isAbi =
                    (!methodLocalName.StartsWith("g_LinkStack")) &&
                    (!methodLocalName.StartsWith("g_UnlinkStack"));

                Hashtable methods = (Hashtable)classTable[className];
                if (methods == null) {
                    methods = new Hashtable();
                    classTable[className] = methods;
                }
                methods[decl] = true;
                string typ = ret + "(*)" + s.Substring(k, s.IndexOf(')', k) + 1 - k);
                if (genEntry && isAbi) {
                    Console.WriteLine("static int f" + n + " = int((" + typ + ")" + methodName + ");");
                }
            }

            i = s.IndexOf('(', i) + 1;
            int smallWords = 0;
            int largeWords = 0;
            while (s[i] != ')') {
                int j = s.IndexOf(',', i);
                if (j == -1) {
                    j = s.IndexOf(')', i);
                }
                string param = s.Substring(i, j - i);
                int size;
                if (param.EndsWith("*") || param.EndsWith("Handle")) {
                    size = 4;
                }
                else {
                    if (!paramTable.ContainsKey(param)) {
                        throw new Exception("unknown parameter type: " + param);
                    }
                    size = (int) paramTable[param];
                }
                //Console.WriteLine(size + ":" + param);
                try {
                    AddType(param);
                }
                catch {
                    Console.WriteLine("Failed at input line {0}\n", lineNumber);
                    throw;
                }
                i = j + 1;
                if (size <= 4) {
                    smallWords++;
                }
                else {
                    largeWords += size / 4;
                }
            }

            int paramRegs = (smallWords < 2) ? (smallWords) : 2;
            int paramSlots = smallWords + largeWords - paramRegs;

            if (genLib && isAbi) {
                Console.WriteLine("__declspec(naked dllexport) " + decl);
                Console.WriteLine("{__asm {");
                Console.WriteLine("    push edx");
                Console.WriteLine("    push ecx");

                Console.WriteLine("    mov ecx, " + n);
                Console.WriteLine("    mov edx, esp");
                Console.WriteLine("    push done");

                Console.WriteLine("    _emit   0x0f;");
                Console.WriteLine("    _emit   0x34;  //sysenter");
                Console.WriteLine("  done:");

                Console.WriteLine("    ret " + 4 * paramSlots);
                Console.WriteLine("}}");
                Console.WriteLine();
            }

            if (genEntry && isAbi) {
                Console.WriteLine("__declspec(naked) static void abi" + n + "() {__asm {");
                for (int j = paramSlots - 1; j >= 0; j--) {
                    Console.WriteLine("    push [eax + " + (12 + j * 4) + "]");
                }
                Console.WriteLine("    mov edx, [eax + 4]");
                Console.WriteLine("    mov ecx, [eax + 0]");
                Console.WriteLine("    call f" + n);
                Console.WriteLine("    ret");
                Console.WriteLine("}}");
            }
            if (isAbi) {
                n++;
            }
        }
        if (genHdr) {
            foreach (string s in structTable.Keys) {
                if (!classTable.ContainsKey(s)) {
                    Console.WriteLine("struct " + s + " {" + structTable[s] + "};");
                }
            }
            Console.WriteLine();
            foreach (string c in classTable.Keys) {
                Console.WriteLine("struct " + c);
                Console.WriteLine("{");
                if (structTable.ContainsKey(c)) {
                    string s = (string) structTable[c];
                    if (s != "") {
                        Console.WriteLine("    " + s);
                    }
                }
                foreach (string m in ((Hashtable)classTable[c]).Keys) {
                    Console.WriteLine("    __declspec(dllexport) static " + m + ";");
                }
                Console.WriteLine("};");
                Console.WriteLine();
            }
        }
        if (genEntry) {
            Console.WriteLine();
            Console.WriteLine("void EnterRing0();");
            Console.WriteLine("int syscallEntryTable[] =");
            Console.WriteLine("{");
            Console.WriteLine("int(EnterRing0)");
            for (int i = 1; i < n; i++) {
                if (i > 0) {
                    Console.WriteLine(",");
                }
                Console.Write("    int(abi" + i + ")");
            }
            Console.WriteLine();
            Console.WriteLine("};");
        }
    }

    public static int Main(string[] args)
    {
        try {
            Generate(args);
        }
        catch (Exception e) {
            Console.Write(e);
            return -1;
        }
        return 0;
    }
}
