<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Signum.Utilities;
using System.Windows.Automation;
using System.Linq.Expressions;
using Signum.Utilities.ExpressionTrees;
using Signum.Entities;

namespace Signum.Windows.UIAutomation
{
    public static class WaitExtensions
    {
        public static int DefaultSleep = 200;
        public static int DefaultTimeout = 2 * 1000;

        public static void Wait(this AutomationElement automationElement, Func<bool> waitCondition, Func<string> actionDescription, int? timeOut = null)
        {
            long start = PerfCounter.Ticks;

            if (waitCondition())
                return;

            while (true)
            {
                Thread.Sleep(DefaultSleep);

                if (waitCondition())
                    return;

                if (((PerfCounter.Ticks - start) / PerfCounter.FrequencyMilliseconds) > (timeOut ?? DefaultTimeout))
                    throw new TimeoutException("Wait condition failed after {0} ms: ".Formato(timeOut ?? DefaultTimeout) + actionDescription == null ? null : actionDescription());
            }
        }

        public static void WaitDataContextChangedAfter(this AutomationElement element, Action action, int? timeOut = null, Func<string> actionDescription = null)
        {
            string oldValue = element.Current.ItemStatus;
            if (string.IsNullOrEmpty(oldValue))
                throw new InvalidOperationException("Element does not has ItemStatus set. Consider setting m:Common.AutomationItemStatusFromDataContext on the WPF control");

            var pid = element.Current.ProcessId;
            int c = 0;
            var previous = GetAllProcessWindows(pid, c++).Select(a => a.GetRuntimeId().ToString(".")).ToHashSet();

            action();

            if (actionDescription == null)
                actionDescription = () => "DataContextChanged for {0}".Formato(oldValue);
         
            element.Wait(() =>
            {
                var newValue = element.Current.ItemStatus;
                if (newValue != null && newValue != oldValue)
                    return true;

                var newWindow = GetAllProcessWindows(pid, c++).FirstOrDefault(a => !previous.Contains(a.GetRuntimeId().ToString(".")));

                MessageBoxProxy.ThrowIfError(newWindow);

                return false;
            }, actionDescription, timeOut);
        }

        public static void WaitDataContextSet(this AutomationElement element, Func<string> actionDescription = null, int? timeOut = null)
        {
            if (actionDescription == null)
                actionDescription = () => "Has DataContext";

            element.Wait(() =>
            {
                var newValue = element.Current.ItemStatus;
                if (newValue.HasText())
                    return true;

                element.AssertMessageBoxChild();

                return false;
            }, actionDescription, timeOut);
        }

        public static int CapturaWindowTimeout = 5 * 1000;


        public static AutomationElement CaptureWindow(this AutomationElement element, Action action, Func<string> actionDescription = null, int? timeOut = null)
        {
            if (actionDescription == null)
                actionDescription = () => "Get Windows after";

            var pid = element.Current.ProcessId;
            int c = 0;
            var previous = GetAllProcessWindows(pid, c++).Select(a => a.GetRuntimeId().ToString(".")).ToHashSet();

            action();

            AutomationElement newWindow = null;

            element.Wait(() =>
            {
                newWindow = GetAllProcessWindows(pid, c++).FirstOrDefault(a => !previous.Contains(a.GetRuntimeId().ToString(".")));

                MessageBoxProxy.ThrowIfError(newWindow);

                if (newWindow != null)
                    return true;

                return false;
            }, actionDescription, timeOut ?? CapturaWindowTimeout);

            return newWindow;
        }

        public static List<AutomationElement> GetAllProcessWindows(int processId, int retry = -1)
        {
            var result = GetRecursiveProcessWindows(AutomationElement.RootElement, processId);
            lock (Entries)
            {
                Entries.AddRange(result.Select(r =>
                {
                    try
                    {
                        return new LogEntry
                        {
                            retry = retry,
                            ClassName = r.Current.ClassName,
                            Name = r.Current.Name,
                            RuntimeId = r.GetRuntimeId().ToString("."),
                            ItemStatus = r.Current.ItemStatus,
                            ThreadId = Thread.CurrentThread.ManagedThreadId,
                        };
                    }
                    catch (ElementNotAvailableException)
                    {
                        return new LogEntry
                        {
                            retry = retry,
                            ClassName = "error",
                            Name = "error",
                            RuntimeId = r.GetRuntimeId().ToString("."),
                            ItemStatus = "error",
                            ThreadId = Thread.CurrentThread.ManagedThreadId,
                        };
                    }
                }));
            }

            return result;
        }

        class LogEntry
        {
            public int retry;
            public string ClassName;
            public string Name;
            public string RuntimeId;
            public string ItemStatus;
            public int ThreadId;
        }

        public static void PrintEntries(Exception e, string should, string current)
        {
            lock (Entries)
            {
                Entries.Add(new LogEntry { retry = 987, ClassName = should, Name = current, ItemStatus = e.Message, RuntimeId = e.StackTrace, ThreadId = Thread.CurrentThread.ManagedThreadId });
                Entries.ToCsvFile(@"c:\debugWindows.csv", append: true);
                Entries.Clear();
            }
        }

        static List<LogEntry> Entries = new List<LogEntry>();

        static List<AutomationElement> GetRecursiveProcessWindows(AutomationElement parentWindow, int processId)
        {
            var children = parentWindow.Children(a => a.Current.ProcessId == processId && a.Current.ControlType == ControlType.Window);
         <commit_msg>WaitExtensions.PrintEntries 4<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Signum.Utilities;
using System.Windows.Automation;
using System.Linq.Expressions;
using Signum.Utilities.ExpressionTrees;
using Signum.Entities;
using System.Reflection;

namespace Signum.Windows.UIAutomation
{
    public static class WaitExtensions
    {
        public static int DefaultSleep = 200;
        public static int DefaultTimeout = 2 * 1000;

        public static void Wait(this AutomationElement automationElement, Func<bool> waitCondition, Func<string> actionDescription, int? timeOut = null)
        {
            long start = PerfCounter.Ticks;

            if (waitCondition())
                return;

            while (true)
            {
                Thread.Sleep(DefaultSleep);

                if (waitCondition())
                    return;

                if (((PerfCounter.Ticks - start) / PerfCounter.FrequencyMilliseconds) > (timeOut ?? DefaultTimeout))
                    throw new TimeoutException("Wait condition failed after {0} ms: ".Formato(timeOut ?? DefaultTimeout) + actionDescription == null ? null : actionDescription());
            }
        }

        public static void WaitDataContextChangedAfter(this AutomationElement element, Action action, int? timeOut = null, Func<string> actionDescription = null)
        {
            string oldValue = element.Current.ItemStatus;
            if (string.IsNullOrEmpty(oldValue))
                throw new InvalidOperationException("Element does not has ItemStatus set. Consider setting m:Common.AutomationItemStatusFromDataContext on the WPF control");

            var pid = element.Current.ProcessId;
            int c = 0;
            var previous = GetAllProcessWindows(pid, c).Select(a => a.GetRuntimeId().ToString(".")).ToHashSet();
            c++;
            action();

            if (actionDescription == null)
                actionDescription = () => "DataContextChanged for {0}".Formato(oldValue);
         
            element.Wait(() =>
            {
                var newValue = element.Current.ItemStatus;
                if (newValue != null && newValue != oldValue)
                    return true;

                var newWindow = GetAllProcessWindows(pid, c).FirstOrDefault(a => !previous.Contains(a.GetRuntimeId().ToString(".")));
                c++;
                MessageBoxProxy.ThrowIfError(newWindow);

                return false;
            }, actionDescription, timeOut);
        }

        public static void WaitDataContextSet(this AutomationElement element, Func<string> actionDescription = null, int? timeOut = null)
        {
            if (actionDescription == null)
                actionDescription = () => "Has DataContext";

            element.Wait(() =>
            {
                var newValue = element.Current.ItemStatus;
                if (newValue.HasText())
                    return true;

                element.AssertMessageBoxChild();

                return false;
            }, actionDescription, timeOut);
        }

        public static int CapturaWindowTimeout = 5 * 1000;


        public static AutomationElement CaptureWindow(this AutomationElement element, Action action, Func<string> actionDescription = null, int? timeOut = null)
        {
            if (actionDescription == null)
                actionDescription = () => "Get Windows after";

            var pid = element.Current.ProcessId;
            int c = 0;
            var previous = GetAllProcessWindows(pid, c).Select(a => a.GetRuntimeId().ToString(".")).ToHashSet();
            c++;
            action();

            AutomationElement newWindow = null;

            element.Wait(() =>
            {
                newWindow = GetAllProcessWindows(pid, c).FirstOrDefault(a => !previous.Contains(a.GetRuntimeId().ToString(".")));
                c++;
                MessageBoxProxy.ThrowIfError(newWindow);

                if (newWindow != null)
                    return true;

                return false;
            }, actionDescription, timeOut ?? CapturaWindowTimeout);

            return newWindow;
        }

        public static List<AutomationElement> GetAllProcessWindows(int processId, int retry = -10)
        {
            var result = GetRecursiveProcessWindows(AutomationElement.RootElement, processId);
            lock (Entries)
            {
                Entries.AddRange(result.Select(r =>
                {
                    try
                    {
                        return new LogEntry
                        {
                            retry = retry,
                            ClassName = r.Current.ClassName,
                            Name = r.Current.Name,
                            RuntimeId = r.GetRuntimeId().ToString("."),
                            ItemStatus = r.Current.ItemStatus,
                            StackTrace = Environment.StackTrace,
                        };
                    }
                    catch (ElementNotAvailableException)
                    {
                        return new LogEntry
                        {
                            retry = retry,
                            ClassName = "error",
                            Name = "error",
                            RuntimeId = r.GetRuntimeId().ToString("."),
                            ItemStatus = "error",
                            StackTrace = Environment.StackTrace,
                        };
                    }
                }));
            }

            return result;
        }

        class LogEntry
        {
            public int retry;
            public string ClassName;
            public string Name;
            public string RuntimeId;
            public string ItemStatus;
            public string StackTrace;
        }

        public static void PrintEntries(Exception e, string should, string current)
        {
            lock (Entries)
            {
                Entries.Add(new LogEntry { retry = 987, ClassName = should, Name = current, ItemStatus = e.Message,  StackTrace = Environment.StackTrace });
                Entries.ToCsvFile(@"c:\debugWindows.csv", append: true);
                Entries.Clear();
            }
        }

        static List<LogEntry> Entries = new List<LogEntry>();

        static List<AutomationElement> GetRecursiveProcessWindows(AutomationElement parentWindow, int processId)
        {
            var children = parentWindow.Children(a => a.Current.ProcessId == processId && a.Current.ControlType == ControlType.Window);
         
{'fraction_non_alphanumeric': 0.08886041603858909, 'fraction_numerical': 0.003316249623153452, 'mean_word_length': 1.598707403055229, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3518538', 'n_tokens_mistral': 3159, 'n_tokens_neox': 2967, 'n_words': 733}