private void DrawProfilerTop15(Listing_Extended lister, Settings settings)
        {
            lister.LabelColored($"Top 15 (Triggered:{PatchHandler.ProfiledRecordsCount()} Methods:{Patcher.PatchedMethodsCount()})", TitleLabelColor);

            int maxRecordCount = 15;

            // update cache every 1 sec and skip update if mouse on buttons
            if (cached == null || !stopUpdate && cacheUpdateTimer.ElapsedMilliseconds > 1000L)
            {
                cached = PatchHandler.GetProfileRecordsSorted()
                         .Where(x => !hided.Any(x.MethodName.Equals))
                         //.OrderByDescending(x => x.TimeSpent)
                         .Take(maxRecordCount)
                         .ToList();
                cacheUpdateTimer = Stopwatch.StartNew();
            }

            // draw cached info
            stopUpdate = false;
            var backFont = Text.Font;

            foreach (var r in cached)
            {
                string tooltip = r.Tooltip;

                Rect buttonRect1 = lister.GetRect(Text.LineHeight),
                     buttonRect2 = buttonRect1,
                     buttonRect3 = buttonRect1,
                     buttonRect4 = buttonRect1,
                     buttonRect5 = buttonRect1;

                buttonRect1.width = buttonRect2.width = buttonRect3.width = buttonRect4.width = 40;

                buttonRect2.x = buttonRect1.xMax;
                buttonRect3.x = buttonRect2.xMax;
                buttonRect4.x = buttonRect3.xMax;

                buttonRect5.width -= 40 * 4;
                buttonRect5.x      = buttonRect4.xMax;

                if (ButtonText(buttonRect1, "Copy", tooltip + "\nPress for copy this method name", out bool button1IsMouseOver))
                {
                    if (!settings.profileCustom.Contains(r.MethodName))
                    {
                        bool addLine = !settings.profileCustom.IsNullOrEmptyOrEqual(Settings.CustomExampleStr);
                        if (addLine)
                        {
                            settings.profileCustom += $"\n{r.MethodName}";
                        }
                        else
                        {
                            settings.profileCustom = $"{r.MethodName}";
                        }
                    }
                }

                bool logActive = PatchHandler.logMethod != null && r.Method == PatchHandler.logMethod;
                if (ButtonText(buttonRect2, logActive ? "X" : "Log", tooltip + "\nPress for copy this method name", out bool button2IsMouseOver))
                {
                    // disable log this method
                    if (logActive)
                    {
                        PatchHandler.logMethod = null;
                    }
                    // enable log this method
                    else
                    {
                        PatchHandler.logMethod = r.Method;
                    }
                }

                bool isDisabled = PatchDisabler.IsDisabled(r.Method);
                if (ButtonText(buttonRect3, isDisabled ? "On" : "Off", tooltip + (isDisabled ? "\nPress for ENABLE this method" : "\nPress for DISABLE this method"), out bool button3IsMouseOver))
                {
                    if (isDisabled)
                    {
                        PatchDisabler.EnableMethod(r.Method);
                    }
                    else
                    {
                        PatchDisabler.DisableMethod(r.Method);
                    }
                }

                if (ButtonText(buttonRect4, "Undo", tooltip + ("\nPress for REMOVE PROFILER for this method"), out bool button4IsMouseOver))
                {
                    Patcher.UnpatchMethod(r.Method);
                    cached = null;
                    break; // and redraw
                }

                if (ButtonText(buttonRect5, r.MethodName, tooltip + "\nPress for hide this line", out bool button5IsMouseOver))
                {
                    hided.Add(r.MethodName);
                    cached = null;
                    break; // and redraw
                }

                if (button1IsMouseOver || button2IsMouseOver || button3IsMouseOver || button4IsMouseOver || button5IsMouseOver)
                {
                    stopUpdate = true;
                }

                lister.Label($"  TimeSpent:{r.TimeSpent}ms AvgTick:{r.AvgTime:0.00000}ms Ticks:{r.TicksNum}");
            }
            Text.Font = backFont;

            if (hided.Count > 0 && lister.ButtonText($"Reset Hided", Text.LineHeight))
            {
                hided.Clear();
                cached = null;
            }
        }
        private void DrawOther(Listing_Extended lister, Settings settings)
        {
            if (lister.ButtonText($"Results", Text.LineHeight))
            {
                Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>
                {
                    new FloatMenuOption("Dump to SLK", () => {
                        List <StopwatchRecord> result = PatchHandler.GetProfileRecordsSorted();
                        FS.WriteAllText($"Profiler_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.slk", result.DumpToSlk());
                    }),
                    new FloatMenuOption("Dump to CSV", () => {
                        List <StopwatchRecord> result = PatchHandler.GetProfileRecordsSorted();
                        FS.WriteAllText($"Profiler_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.csv", result.DumpToCsv());
                    }),
                    new FloatMenuOption("Reset results", () => {
                        PatchHandler.Reset();
                    }),
                }));
            }
            if (lister.ButtonText($"Tools / Other", Text.LineHeight))
            {
                IEnumerable <FloatMenuOption> getOptions()
                {
                    yield return(new FloatMenuOption($"Enable disabled methods: {PatchDisabler.DisabledCount}", () =>
                    {
                        PatchDisabler.EnableAllDisabled();
                    }));

                    yield return(new FloatMenuOption("Dump all harmony patches", () =>
                    {
                        FS.WriteAllText("HarmonyPatches.txt", HarmonyMain.AllHarmonyPatchesDump());
                        FS.WriteAllText("HarmonyPatches-Conflicts.txt", HarmonyMain.CanConflictHarmonyPatchesDump());
                    }));
                    // if (DubsProfilerReset.CanUnpatch())
                    // {
                    //     yield return new FloatMenuOption("Reset DubsPerfomanceAnalyzer patches", DubsProfilerReset.ResetProfiler);
                    // }
                }

                Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>(getOptions())));
            }

            // Method resolver. Handle string >= 3 chars. Results show after 2 sec. when str not changed. Has filter AND, example: 'pawn tick' => Verse.Pawn:Tick
            {
                var  prevStr      = _methodResolver;
                Rect labelRect    = lister.GetRect(Text.LineHeight),
                     textEntyRect = labelRect;

                float width = labelRect.width;
                labelRect.width    = width / 3;
                textEntyRect.width = 2 * width / 3;
                textEntyRect.x     = labelRect.xMax;
                Widgets.Label(labelRect, $"Method resolver");
                _methodResolver = Widgets.TextArea(textEntyRect, _methodResolver);
                if (!String.IsNullOrWhiteSpace(_methodResolver) && _methodResolver.Length >= 4)
                {
                    if (!prevStr.Equals(_methodResolver))
                    {
                        _methodResolverInputTimer = Stopwatch.StartNew();
                    }
                }
                else
                {
                    _methodResolverInputTimer = null;
                }

                if (_methodResolverInputTimer != null &&
                    _methodResolverInputTimer.ElapsedMilliseconds > 1500)
                {
                    var strLower   = _methodResolver.ToLower();
                    var arrFilters = strLower.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                    var allMethods = Utils.GetAllMethods();

                    IEnumerable <string> filterMethods()
                    {
                        if (_methodResolverCache == null)
                        {
                            _methodResolverCache = allMethods
                                                   .OrderBy(x => x)
                                                   .Select(x => (x, x.ToLower()))
                                                   .ToList();
                        }
                        foreach (var m in (from tuple in _methodResolverCache
                                           where arrFilters.All(x => tuple.nameLower.Contains(x))
                                           select tuple))
                        {
                            yield return(m.name);
                        }
                    }

                    IEnumerable <FloatMenuOption> makeVariants(int maxResult)
                    {
                        foreach (var m in filterMethods().OrderBy(x => x))
                        {
                            if (maxResult-- < 0)
                            {
                                break;
                            }
                            yield return(new FloatMenuOption(m, () => _methodResolver = m));
                        }
                    }

                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>(makeVariants(20))));
                    _methodResolverInputTimer = null;
                }
            }
            lister.CheckboxLabeled("CRASH DEBUG", ref settings.debug);
            lister.Gap(20f);
        }