internal DotNetScriptFunctions(ScriptReturnData srd, string dataFilesPath, string pluginsPath, ref SharedFunctionsHandler handler)
 {
     _srd       = srd;
     _dataFiles = dataFilesPath;
     _plugins   = pluginsPath;
     _handler   = handler;
 }
        internal static ScriptReturnData ExecuteScript(string script, string dataPath, string pluginsPath, IScriptFunctions scriptFunctions)
        {
            Utils.Info("Starting script execution...");
            if (string.IsNullOrWhiteSpace(script))
            {
                Utils.Error("Script is empty! Returning empty ScriptReturnData");
                return(new ScriptReturnData());
            }

            ScriptType type;

            if ((byte)script[0] >= (byte)ScriptType.Count)
            {
                type = ScriptType.OBMMScript;
            }
            else
            {
                type   = (ScriptType)script[0];
                script = script.Substring(1);
            }

            Utils.Debug($"ScriptType is {type.ToString()}");

            var handler = new SharedFunctionsHandler(type, ref scriptFunctions);
            var srd     = new ScriptReturnData();
            var dotNetScriptFunctions = new Lazy <DotNetScriptFunctions>(() =>
                                                                         new DotNetScriptFunctions(srd, dataPath, pluginsPath, ref handler)).Value;

            switch (type)
            {
            case ScriptType.OBMMScript:
                return(OBMMScriptHandler.Execute(script, dataPath, pluginsPath, ref handler));

            case ScriptType.Python:     //TODO
                break;

            case ScriptType.CSharp:
                DotNetScriptHandler.ExecuteCS(script, ref dotNetScriptFunctions);
                break;

            case ScriptType.VB:
                DotNetScriptHandler.ExecuteVB(script, ref dotNetScriptFunctions);
                break;

            case ScriptType.Count:     //Impossible
                Utils.Error("Impossible switch case triggered, how dafuq did this happen?");
                break;

            default:     //Impossible
                Utils.Error("Impossible switch case triggered, how dafuq did this happen?");
                return(srd);
            }

            return(srd);
        }
        internal static ScriptReturnData Execute(string inputScript, string dataPath, string pluginsPath, ref SharedFunctionsHandler handler)
        {
            Handler = handler;
            Srd     = new ScriptReturnData();
            if (string.IsNullOrWhiteSpace(inputScript))
            {
                return(Srd);
            }

            DataFiles = dataPath;
            Plugins   = pluginsPath;
            Variables = new Dictionary <string, string>();

            var flowControl = new Stack <FlowControlStruct>();

            Variables["NewLine"] = Environment.NewLine;
            Variables["Tab"]     = "\t";

            var    script          = inputScript.Replace("\r", "").Split('\n');
            string skipTo          = null;
            bool   allowRunOnLines = false;
            bool   Break           = false;

            for (var i = 0; i < script.Length || ExtraLines.Count > 0; i++)
            {
                string s;
                if (ExtraLines.Count > 0)
                {
                    i--;
                    s = ExtraLines.Dequeue().Replace('\t', ' ').Trim();
                }
                else
                {
                    s = script[i].Replace('\t', ' ').Trim();
                }

                CLine = i.ToString();
                if (allowRunOnLines)
                {
                    while (s.EndsWith("\\"))
                    {
                        s = s.Remove(s.Length - 1);
                        if (ExtraLines.Count > 0)
                        {
                            s += ExtraLines.Dequeue().Replace('\t', ' ').Trim();
                        }
                        else
                        {
                            if (++i == script.Length)
                            {
                                Warn("Run-on line passed end of script");
                            }
                            else
                            {
                                s += script[i].Replace('\t', ' ').Trim();
                            }
                        }
                    }
                }

                if (skipTo != null)
                {
                    if (s == skipTo)
                    {
                        skipTo = null;
                    }
                    else
                    {
                        continue;
                    }
                }

                IReadOnlyCollection <string> line = SplitLine(s);
                if (line.Count == 0)
                {
                    continue;
                }

                if (flowControl.Count != 0 && !flowControl.Peek().Active)
                {
                    switch (line.ElementAt(0))
                    {
                    case "":
                        Warn("Empty function");
                        break;

                    case "If":
                    case "IfNot":
                        flowControl.Push(new FlowControlStruct(0));
                        break;

                    case "Else":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 0)
                        {
                            flowControl.Peek().Active = flowControl.Peek().Line != -1;
                        }
                        else
                        {
                            Warn("Unexpected Else");
                        }
                        break;

                    case "EndIf":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 0)
                        {
                            flowControl.Pop();
                        }
                        else
                        {
                            Warn("Unexpected EndIf");
                        }
                        break;

                    case "Select":
                    case "SelectMany":
                    case "SelectWithPreview":
                    case "SelectManyWithPreview":
                    case "SelectWithDescriptions":
                    case "SelectManyWithDescriptions":
                    case "SelectWithDescriptionsAndPreviews":
                    case "SelectManyWithDescriptionsAndPreviews":
                    case "SelectVar":
                    case "SelectString":
                        flowControl.Push(new FlowControlStruct(1));
                        break;

                    case "Case":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 1)
                        {
                            if (flowControl.Peek().Line != -1 && Array.IndexOf(flowControl.Peek().Values, s) != -1)
                            {
                                flowControl.Peek().Active  = true;
                                flowControl.Peek().HitCase = true;
                            }
                        }
                        else
                        {
                            Warn("Unexpected Break");
                        }

                        break;

                    case "Default":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 1)
                        {
                            if (flowControl.Peek().Line != -1 && !flowControl.Peek().HitCase)
                            {
                                flowControl.Peek().Active = true;
                            }
                        }
                        else
                        {
                            Warn("Unexpected Default");
                        }

                        break;

                    case "EndSelect":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 1)
                        {
                            flowControl.Pop();
                        }
                        else
                        {
                            Warn("Unexpected EndSelect");
                        }
                        break;

                    case "For":
                        flowControl.Push(new FlowControlStruct(2));
                        break;

                    case "EndFor":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 2)
                        {
                            flowControl.Pop();
                        }
                        else
                        {
                            Warn("Unexpected EndFor");
                        }
                        break;

                    case "Break":
                    case "Continue":
                    case "Exit":
                        break;
                    }
                }
                else
                {
                    var lineMsg = "";
                    line.Do(cur => lineMsg += "'" + cur + "' ");

                    var registry = new SharedFunctionsRegistry(Handler);
                    var function = registry.GetFunctionByName(line.ElementAt(0));
                    if (function != null)
                    {
                        if (line.Count < function.MinArgs)
                        {
                            Warn($"Missing arguments for '{function.FuncName}'");
                            break;
                        }

                        if (function.MaxArgs != 0 && line.Count > function.MaxArgs)
                        {
                            Warn($"Unexpected arguments for '{function.FuncName}'");
                        }

                        Utils.Script($"\"{function.FuncName}\" called with line: {lineMsg}");
                        function.Run(ref line);
                    }
                    else
                    {
                        Utils.Script($"\"{line.ElementAt(0)}\" called with line: {lineMsg}");
                    }
                    switch (line.ElementAt(0))
                    {
                    case "Goto":
                        if (line.Count < 2)
                        {
                            Warn("Not enough arguments to function 'Goto'!");
                        }
                        else
                        {
                            if (line.Count > 2)
                            {
                                Warn("Unexpected extra arguments to function 'Goto'");
                            }
                            skipTo = $"Label {line.ElementAt(1)}";
                            flowControl.Clear();
                        }
                        break;

                    case "Label":
                        break;

                    case "If":
                        flowControl.Push(new FlowControlStruct(i, FunctionIf(line)));
                        break;

                    case "IfNot":
                        flowControl.Push(new FlowControlStruct(i, !FunctionIf(line)));
                        break;

                    case "Else":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 0)
                        {
                            flowControl.Peek().Active = false;
                        }
                        else
                        {
                            Warn("Unexpected Else");
                        }
                        break;

                    case "EndIf":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 0)
                        {
                            flowControl.Pop();
                        }
                        else
                        {
                            Warn("Unexpected EndIf");
                        }
                        break;

                    case "Select":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, false, false, false)));
                        break;

                    case "SelectMany":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, true, false, false)));
                        break;

                    case "SelectWithPreview":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, false, true, false)));
                        break;

                    case "SelectManyWithPreview":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, true, true, false)));
                        break;

                    case "SelectWithDescriptions":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, false, false, true)));
                        break;

                    case "SelectManyWithDescriptions":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, true, false, true)));
                        break;

                    case "SelectWithDescriptionsAndPreviews":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, false, true, true)));
                        break;

                    case "SelectManyWithDescriptionsAndPreviews":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelect(line, true, true, true)));
                        break;

                    case "SelectVar":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelectVar(line, true)));
                        break;

                    case "SelectString":
                        flowControl.Push(new FlowControlStruct(i, FunctionSelectVar(line, false)));
                        break;

                    case "Break": {
                        bool found = false;
                        var  fcs   = flowControl.ToArray();
                        for (int k = 0; k < fcs.Length; k++)
                        {
                            if (fcs[k].Type != 1)
                            {
                                continue;
                            }

                            for (int j = 0; j <= k; j++)
                            {
                                fcs[j].Active = false;
                            }
                            found = true;
                            break;
                        }

                        if (!found)
                        {
                            Warn("Unexpected Break");
                        }
                        break;
                    }

                    case "Case":
                        if (flowControl.Count == 0 || flowControl.Peek().Type != 1)
                        {
                            Warn("Unexpected Case");
                        }
                        break;

                    case "Default":
                        if (flowControl.Count == 0 || flowControl.Peek().Type != 1)
                        {
                            Warn("Unexpected Default");
                        }
                        break;

                    case "EndSelect":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 1)
                        {
                            flowControl.Pop();
                        }
                        else
                        {
                            Warn("Unexpected EndSelect");
                        }
                        break;

                    case "For": {
                        var fc = FunctionFor(line, i);
                        flowControl.Push(fc);
                        if (fc.Line != -1 && fc.Values.Length > 0)
                        {
                            Variables[fc.Var] = fc.Values[0];
                            fc.Active         = true;
                        }

                        break;
                    }

                    case "Continue": {
                        var found = false;
                        var fcs   = flowControl.ToArray();
                        for (int k = 0; k < fcs.Length; k++)
                        {
                            if (fcs[k].Type != 2)
                            {
                                continue;
                            }

                            fcs[k].ForCount++;
                            if (fcs[k].ForCount == fcs[k].Values.Length)
                            {
                                for (int j = 0; j <= k; j++)
                                {
                                    fcs[j].Active = false;
                                }
                            }
                            else
                            {
                                i = fcs[k].Line;
                                Variables[fcs[k].Var] = fcs[k].Values[fcs[k].ForCount];
                                for (int j = 0; j < k; j++)
                                {
                                    flowControl.Pop();
                                }
                            }

                            found = true;
                            break;
                        }

                        if (!found)
                        {
                            Warn("Unexpected Continue");
                        }
                        break;
                    }

                    case "Exit": {
                        bool found = false;
                        var  fcs   = flowControl.ToArray();
                        for (int k = 0; k < fcs.Length; k++)
                        {
                            if (fcs[k].Type != 2)
                            {
                                continue;
                            }

                            for (int j = 0; j <= k; j++)
                            {
                                flowControl.Peek().Active = false;
                            }
                            found = true;
                            break;
                        }

                        if (!found)
                        {
                            Warn("Unexpected Exit");
                        }
                        break;
                    }

                    case "EndFor":
                        if (flowControl.Count != 0 && flowControl.Peek().Type == 2)
                        {
                            var fc = flowControl.Peek();
                            fc.ForCount++;
                            if (fc.ForCount == fc.Values.Length)
                            {
                                flowControl.Pop();
                            }
                            else
                            {
                                i = fc.Line;
                                Variables[fc.Var] = fc.Values[fc.ForCount];
                            }
                        }
                        else
                        {
                            Warn("Unexpected EndFor");
                        }
                        break;

                    //Functions
                    case "DontInstallAnyPlugins":
                        Srd.InstallAllPlugins = false;
                        break;

                    case "DontInstallAnyDataFiles":
                        Srd.InstallAllData = false;
                        break;

                    case "InstallAllPlugins":
                        Srd.InstallAllPlugins = true;
                        break;

                    case "InstallAllDataFiles":
                        Srd.InstallAllData = true;
                        break;

                    case "FatalError":
                        Utils.Error("Script called FatalError!");
                        Srd.CancelInstall = true;
                        break;

                    case "Return":
                        Break = true;
                        break;

                    case "AllowRunOnLines":
                        allowRunOnLines = true;
                        break;

                    default:
                        break;
                    }
                }

                if (Break || Srd.CancelInstall)
                {
                    break;
                }
            }

            if (skipTo != null)
            {
                Warn($"Expected: {skipTo}!");
            }

            var temp = Srd;

            Srd       = null;
            Variables = null;

            return(temp);
        }