using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;

namespace ZKuP
{
    class Helper
    {
        public static double width = 0;
        public static double height = 0;
        public static bool? IsFileLocked(FileInfo file)
        {
            try
            {
                using (FileStream stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    stream.Close();
                }
            }
            catch (FileNotFoundException)
            {
                Log.WriteLog($"File does not exist: {file.Name}");
                return null;
            }
            catch (IOException)
            {
                //the file is unavailable because it is:
                //still being written to
                //or being processed by another thread
                //or does not exist (has already been processed)
                return true;
            }

            //file is not locked
            return false;
        }

        public static bool IsVPNConnected()
        {
            var available = false;
            if (NetworkInterface.GetIsNetworkAvailable())
            {
                NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface Interface in interfaces)
                {
                    if (Interface.OperationalStatus == OperationalStatus.Up)
                    {

                        IPv4InterfaceStatistics statistics = Interface.GetIPv4Statistics();

                        if (Interface.Name == "Pulse Secure")
                            available = true;
                    }
                }

                if (available)
                    return true;
                else
                {
                    return false;
                }
            }
            else return false;
        }

        private static List<string> GetBEs()
        {
            var asm = Assembly.GetEntryAssembly();
            string resName = asm.GetName().Name + ".g.resources";
            using (var stream = asm.GetManifestResourceStream(resName))
            using (var reader = new System.Resources.ResourceReader(stream))
            {
                var res = reader.Cast<DictionaryEntry>().Select(entry => (string)entry.Key).Where(p => p.StartsWith("assets/be")).OrderBy(p => p).ToList();
                return ReplaceSpecialCharacters(res);
            }
        }

        public static List<string> GetBEFileNames()
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            BEs.ForEach(p => p.Replace("assets/", ""));

            var res = BEs;
            return res;
        }

        public static string GetBEFileNames(string beflaeche)
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            BEs.ForEach(p => p.Replace("assets/", ""));
            return BEs.Where(p => p.Contains(beflaeche)).FirstOrDefault();
        }

        public static string GetBEFileNames(int beNr)
        {
            if (beNr > 0)
            {
                List<string> BEs = new List<string>();

                BEs = GetBEs();

                BEs.ForEach(p => p.Replace("assets/", ""));
                return BEs.Where(p => p.Contains(beNr.ToString().PadLeft(2, '0'))).First();
            }

            return "";
        }

        public static List<string> GetBENames()
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            var res = BEs.Select(p => p.Split(' ')[0].Replace("assets/", "")).ToList();
            return res;
        }

        public static List<string> GetBENumbers()
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            var res = BEs.Select(p => p.Split(' ')[0].Replace("assets/", "").Replace("be", "")).ToList();
            return res;
        }

        public static List<string> GetBEDescription()
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            var res = BEs.Select(p => p.Substring(p.IndexOf("-") + 2,p.Length - (p.IndexOf("-") + 2)).Trim().Replace("_","/").Replace(".png", "").ToUpperInvariant()).ToList();
            return res;
        }

        public static string GetBEDescription(int beNr)
        {
            List<string> BEs = new List<string>();

            BEs = GetBEs();

            var res = BEs.Select(p => p.Substring(p.IndexOf("/") + 1, p.Length - (p.IndexOf("/") + 1)).Trim().Replace("_", "/").Replace(".png", "").ToUpperInvariant());
            var resNr = res.Where(q => q.Contains(beNr.ToString().PadLeft(2, '0'))).First();

            return resNr;
        }

        public static bool IsValidEmailAddress(string s)
        {
            Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
            return regex.IsMatch(s);
        }


        private static List<string> ReplaceSpecialCharacters(List<string> bEs)
        {
            List<string> res = new List<string>();

            foreach (var e in bEs)
            {
                res.Add(e.Replace("%20", " ").Replace("%c3%bc", "ü").Replace("%c3%a4", "ä").Replace("%c3%b6", "ö"));
            }

            return res;
        }




        public static void CheckWindowIsInScreenSpace(Window sender)
        {
            var screen = WpfScreenHelper.Screen.FromHandle(new WindowInteropHelper(sender).Handle);
            var workingArea = screen.WorkingArea;

            // Maximale Top-Position, sodass das Fenster unten nicht rausgeht
            if (sender.Top + sender.ActualHeight > workingArea.Bottom)
            {
                sender.Top = workingArea.Bottom - sender.ActualHeight;
            }

            // Optionale obere Begrenzung
            if (sender.Top < workingArea.Top)
            {
                sender.Top = workingArea.Top;
            }

            if (sender.Left + sender.ActualWidth > workingArea.Right)
            {
                sender.Left = workingArea.Right - sender.ActualWidth;
            }

            // Optionale obere Begrenzung
            if (sender.Left < workingArea.Left)
            {
                sender.Left = workingArea.Left;
            }
        }

        public static void CheckWindowIsInScreenSpace(MahApps.Metro.Controls.MetroWindow sender)
        {
            var screen = WpfScreenHelper.Screen.FromHandle(new WindowInteropHelper(sender).Handle);
            var workingArea = screen.WorkingArea;

            // Maximale Top-Position, sodass das Fenster unten nicht rausgeht
            if (sender.Top + (sender.ActualHeight * .1) > workingArea.Bottom)
            {
                sender.Top = workingArea.Bottom - (sender.ActualHeight * .1);
            }

            // Optionale obere Begrenzung
            if (sender.Top < workingArea.Top)
            {
                sender.Top = workingArea.Top;
            }

            if (sender.Left + (sender.ActualWidth * .1) > workingArea.Right)
            {
                sender.Left = workingArea.Right - (sender.ActualWidth * .1);
            }

            // Optionale obere Begrenzung
            if ((sender.Left + sender.ActualWidth * .8) < workingArea.Left)
            {
                sender.Left = workingArea.Left - (sender.ActualWidth * .8);
            }
        }





        public static List<Tuple<string, string>> DataTableToTuple(System.Data.DataTable dataTable)
        {
            List<Tuple<string, string>> x = new List<Tuple<string, string>>();

            foreach (System.Data.DataRow dr in dataTable.Rows)
            {
                x.Add(new Tuple<string, string>(dr[0].ToString(), dr[1].ToString()));
            }

            return x;
        }


        public static List<string> DataTableToListString(System.Data.DataTable dataTable, int Columns = 1, int startColumn = 0)
        {
            switch (Columns)
            {
                case 1:
                    return (from System.Data.DataRow dr in dataTable.Rows select dr[startColumn + 0].ToString()).ToList();
                    //break;
                case 2:
                    return (from System.Data.DataRow dr in dataTable.Rows select dr[startColumn + 0].ToString() + " " + dr[startColumn + 1].ToString()).ToList();
                    //break;


                default:
                    return (from System.Data.DataRow dr in dataTable.Rows select dr[0].ToString()).ToList();
            }
        }

        public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        yield return (T)child;
                    }

                    foreach (T childOfChild in FindVisualChildren<T>(child))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }

        public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj,
                                         string name) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T &&
                          (child as FrameworkElement).Name.Equals(name))
                    {
                        yield return (T)child;
                    }

                    foreach (T childOfChild in FindVisualChildren<T>(child, name))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }

        public static Parent FindParent<Parent>(DependencyObject child)
          where Parent : DependencyObject
        {
            DependencyObject parentObject = child;

            //We are not dealing with Visual, so either we need to fnd parent or
            //get Visual to get parent from Parent Heirarchy.
            while (!((parentObject is System.Windows.Media.Visual)
                    || (parentObject is System.Windows.Media.Media3D.Visual3D)))
            {
                if (parentObject is Parent || parentObject == null)
                {
                    return parentObject as Parent;
                }
                else
                {
                    parentObject = (parentObject as FrameworkContentElement).Parent;
                }
            }

            //We have not found parent yet , and we have now visual to work with.
            parentObject = VisualTreeHelper.GetParent(parentObject);

            //check if the parent matches the type we're looking for
            if (parentObject is Parent || parentObject == null)
            {
                return parentObject as Parent;
            }
            else
            {
                //use recursion to proceed with next level
                return FindParent<Parent>(parentObject);
            }
        }


        public static DependencyObject GetScrollViewer(DependencyObject o)
        {
            // Return the DependencyObject if it is a ScrollViewer
            if (o is System.Windows.Controls.ScrollViewer)
            { return o; }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
            {
                var child = VisualTreeHelper.GetChild(o, i);

                var result = GetScrollViewer(child);
                if (result == null)
                {
                    continue;
                }
                else
                {
                    return result;
                }
            }
            return null;
        }



        public static void SendMail(string body = "")
        {
            if (body != "") body = "Exception:%0D%0A%0D%0A" + body;

            System.Diagnostics.Process.Start($"mailto:marcus.bachler@deutschebahn.com?subject=Fehlermeldung_ZKuP&body=" + body);
        }



        public static string GetNameFromMail(string _mail)
        {
            var mail = _mail.Split('@')[0];
            var name = "";

            if (mail.Count(p => p == '.') == 0)
                name = mail;
            else if (mail.Count(p => p == '.') == 1)
                name = Helper.InsertSpaceBeforeUpperCase($"{Helper.FirstCharToUpperCase(mail.Split('.')[0])}{Helper.FirstCharToUpperCase(mail.Split('.')[1])}");
            else if (mail.Count(p => p == '.') == 2)
                name = Helper.InsertSpaceBeforeUpperCase($"{Helper.FirstCharToUpperCase(mail.Split('.')[0])}{Helper.FirstCharToUpperCase(mail.Split('.')[2])}");
            else
            {
                MessageBox.Show("Email Adresse konnte nicht erkannt werden", "Fehlerhafte Mail Adresse", MessageBoxButton.OK, MessageBoxImage.Error);
                return "-1";
            }

            if (name.Contains('-'))
                name = Helper.AfterDashToUpperCase(name);

            return name;
        }

        public static string FirstCharToUpperCase(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return string.Empty;
            }

            return $"{input[0].ToString().ToUpper()}{input.Substring(1)}";
        }

        public static string AfterDashToUpperCase(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return string.Empty;
            }

            if (!input.Contains("-"))
                return input;

            var result = "";
            for (var word = 1; word < input.Split('-').Length; word++)
            {
                if (word == 1) result = input.Split('-')[0] + $"-{input.Split('-')[word][0].ToString().ToUpper() + input.Split('-')[word].ToString().Substring(1)}";
                else
                    result = result + $"-{input.Split('-')[word][0].ToString().ToUpper() + input.Split('-')[word].ToString().Substring(1)}";
            }

            return result;
        }
        public static string InsertSpaceBeforeUpperCase(string str)
        {
            var sb = new StringBuilder();

            char previousChar = char.MinValue; // Unicode '\0'

            foreach (char c in str)
            {
                if (char.IsUpper(c))
                {
                    // If not the first character and previous character is not a space, insert a space before uppercase

                    if (sb.Length != 0 && previousChar != ' ')
                    {
                        sb.Append(' ');
                    }
                }

                sb.Append(c);

                previousChar = c;
            }

            return sb.ToString();
        }

        /// <summary>
        /// Removes specifed "stringToRemove" from End of String "input", if "input" ends with "stringToRemove"
        /// </summary>
        /// <param name="input">String that should be Trimmed</param>
        /// <param name="stringToRemove">String value which should be removed</param>
        /// <returns></returns>
        public static string RemoveStringAtEndOfString(string input, string stringToRemove)
        {
            return input.EndsWith(stringToRemove)
                ? input.Substring(0, input.Length - stringToRemove.Length)
                : input;
        }

        /// <summary>
        /// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'.
        /// </summary>
        /// <param name="pathInApplication">Path without starting slash</param>
        /// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param>
        /// <returns></returns>
        public static System.Drawing.Bitmap LoadBitmapFromResource(string pathInApplication, System.Reflection.Assembly assembly = null)
        {
            if (assembly == null)
            {
                assembly = System.Reflection.Assembly.GetCallingAssembly();
            }

            if (pathInApplication[0] == '/')
            {
                pathInApplication = pathInApplication.Substring(1);
            }
            return BitmapImage2Bitmap(new System.Windows.Media.Imaging.BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute)));
        }

        public static System.Drawing.Bitmap BitmapImage2Bitmap(System.Windows.Media.Imaging.BitmapImage bitmapImage)
        {
            // BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));

            using (System.IO.MemoryStream outStream = new System.IO.MemoryStream())
            {
                System.Windows.Media.Imaging.BitmapEncoder enc = new System.Windows.Media.Imaging.TiffBitmapEncoder();
                enc.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bitmapImage, null, null, null));
                enc.Save(outStream);
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);

                return new System.Drawing.Bitmap(bitmap);
            }
        }


        /// <summary>
        /// Takes a bitmap and converts it to an image that can be handled by WPF ImageBrush
        /// </summary>
        /// <param name="src">A bitmap image</param>
        /// <returns>The image as a BitmapImage for WPF</returns>
        public static BitmapImage ConvertBitmapToImage(System.Drawing.Bitmap src)
        {
            try
            {
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                src.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);
                BitmapImage image = new BitmapImage();
                image.BeginInit();
                ms.Seek(0, System.IO.SeekOrigin.Begin);
                image.StreamSource = ms;
                image.EndInit();
                return image;
            }
            catch (Exception ex)
            {
                Log.WriteLog(ex.ToString());
            }

            return null;
        }


        public static BitmapImage ConvertByteArrayToBitmapImage(Byte[] bytes)
        {
            var stream = new System.IO.MemoryStream(bytes);
            stream.Seek(0, System.IO.SeekOrigin.Begin);
            var image = new System.Windows.Media.Imaging.BitmapImage();
            image.BeginInit();
            image.StreamSource = stream;
            image.EndInit();
            return image;
        }
        public static byte[] ConvertImageToByteArray(System.Drawing.Image img)
        {
            ImageConverter converter = new ImageConverter();
            return (byte[])converter.ConvertTo(img, typeof(byte[]));
        }

        public static bool OpenAndEditWord(string Name, string Kennzeichen, string Firma, string Nr, int isKrad = 0, string OE = "")
        {
            try
            {
                using (var regWord = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("Word.Application"))
                {
                    if (regWord == null)
                    {
                        MessageBox.Show("Microsoft Word wird benötigt, ist aber nicht installiert\nFunktion kann nicht ausgeführt werden", "Word nicht installiert", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                    else
                    {
                        //Console.WriteLine("Microsoft Word is installed");

                        Type wordType = Type.GetTypeFromProgID("Word.Application");
                        dynamic wordApp = Activator.CreateInstance(wordType);

                        //Word.Application wordApp = new Word.Application();

                        wordApp.Visible = true;

                        dynamic document;
                        if (isKrad == 0)
                        {
                            document = wordApp.Documents.OpenNoRepairDialog("https://dbsw.sharepoint.com/:w:/r/sites/PfoertnerWerkMuenche/Freigegebene%20Dokumente/Zutrittskontrolle/Parkkarte_Template_ab_2020.dotx");



                            document.Activate();

                            //Word.Table table = document.Tables[1];
                            dynamic table = document.Tables[1];
                            table.Cell(1, 2).Range.Text = Nr;
                            table.Cell(3, 2).Range.Text = Kennzeichen;
                            table.Cell(4, 2).Range.Text = Firma;

                            //Word.Table table2 = document.Tables[2];
                            dynamic table2 = document.Tables[2];
                            table2.Cell(1, 1).Range.Text = Name;

                            //Word.Dialog dialog = null;
                            dynamic dialog = null;
                            dialog = wordApp.Dialogs[88];/*Word.WdWordDialog.wdDialogFilePrint*/
                            var dialogResult = dialog.Show();
                            if (dialogResult == 1)
                            {
                                document.PrintOut(false);
                            }
                            else if (dialogResult == -1)
                            {
                                document.Close(0);/*Word.WdSaveOptions.wdDoNotSaveChanges*/
                                wordApp.Quit();
                            }
                        }
                        else if (isKrad == 1)
                        {
                            document = wordApp.Documents.OpenNoRepairDialog("https://dbsw.sharepoint.com/:w:/r/sites/PfoertnerWerkMuenche/Freigegebene%20Dokumente/Zutrittskontrolle/Parkausweis_Kraftrad_DinA6.docx");


                            document.Activate();
                                                        
                            dynamic table = document.Tables[1];
                            table.Cell(1, 2).Range.Text = Name;
                            table.Cell(2, 2).Range.Text = OE;
                            table.Cell(3, 2).Range.Text = Kennzeichen;
                            table.Cell(4, 2).Range.Text = Nr;

                            //Word.Dialog dialog = null;
                            dynamic dialog = null;
                            dialog = wordApp.Dialogs[88];/*Word.WdWordDialog.wdDialogFilePrint*/
                            var dialogResult = dialog.Show();
                            if (dialogResult == 1)
                            {
                                document.PrintOut(false);
                            }
                            else if (dialogResult == -1)
                            {
                                document.Close(0);/*Word.WdSaveOptions.wdDoNotSaveChanges*/
                                wordApp.Quit();
                            }
                        }
                        else
                        {
                            MessageBox.Show("Weder PKW noch Krad?!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                            return false;
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                Log.WriteLog(ex.ToString());
                MessageBox.Show($"Fehlermeldung:\n\n{ex.Message}", "Fehler!", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }

            return true;
        }

        public async static void PrintPDF(string path)
        {
            Process p = new Process();
            p.StartInfo = new ProcessStartInfo()
            {
                CreateNoWindow = true,
                Verb = "print",
                FileName = path //put the path to pdf here
            };
            p.Start();
            await Task.Delay(8000);
            p.CloseMainWindow();
        }



        public static bool OverMaxLength(object value, int maxLength, string regex = "")
        {
            var result = false;

            if (value.ToString().Length >= maxLength)
            {
                result = true;
                return result;
            }
            else result = false;

            if (regex != "")
                if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), regex)) result = true;
                else result = false;

            return result;
        }



        static List<object> treeVisual = new List<object>();
        public static List<object> ShowWPFVisualTree(DependencyObject element)
        {
            treeVisual.Clear();
            return AddElementsAtVisualTree(element, null);
        }
        ///

        /// Adds the elements at visual tree.
        ///
        /// The element. /// The previtem. 
        private static List<object> AddElementsAtVisualTree(DependencyObject element, TreeViewItem previtem)
        {
            TreeViewItem item = new TreeViewItem();
            item.Header = element.GetType().Name;
            item.IsExpanded = true;

            if (previtem == null)
            {
                treeVisual.Add(item);
            }
            else
            {
                previtem.Items.Add(item);
            }

            int totalElementcount = VisualTreeHelper.GetChildrenCount(element);

            for (int counter = 0; counter < totalElementcount; counter++) { AddElementsAtVisualTree(VisualTreeHelper.GetChild(element, counter), item); }

            return treeVisual;
        }

        public static T GetVisualChild<T>(Visual parent) where T : Visual
        {
            T child = default(T);
            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < numVisuals; i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
                child = v as T;
                if (child == null)
                {
                    child = GetVisualChild<T>(v);
                }
                if (child != null)
                {
                    break;
                }
            }
            return child;
        }

        public static DataGridRow GetSelectedRow(DataGrid grid)
        {
            return (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);
        }
        public static DataGridRow GetRow(DataGrid grid, int index)
        {
            DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
            if (row == null)
            {
                // May be virtualized, bring into view and try again.
                grid.UpdateLayout();
                grid.ScrollIntoView(grid.Items[index]);
                row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
            }
            return row;
        }

        public static DataGridCell GetCell(DataGrid grid, DataGridRow row, int column)
        {
            if (row != null)
            {
                DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);

                if (presenter == null)
                {
                    grid.ScrollIntoView(row, grid.Columns[column]);
                    presenter = GetVisualChild<DataGridCellsPresenter>(row);
                }

                DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
                return cell;
            }
            return null;
        }

        public static DataGridCell GetCell(DataGrid grid, int row, int column)
        {
            DataGridRow rowContainer = GetRow(grid, row);
            return GetCell(grid, rowContainer, column);
        }

        static double lastDpiScale = -1;
        static bool _isCurrentlyScaled = false;
        internal static async void CheckScale(Window mainWindow)
        {
            var dpi = VisualTreeHelper.GetDpi(mainWindow);
            double dpiScale = dpi.DpiScaleX; // or DpiScaleY


            double scaledWidth = width * dpiScale;
            double scaledHeight = height * dpiScale;

            var screen = WpfScreenHelper.Screen.FromHandle(new WindowInteropHelper(mainWindow).Handle);
            var workingArea = screen.WorkingArea;

            bool needsScaling = (width * dpiScale) > workingArea.Width || (height * dpiScale) > workingArea.Height;

            lastDpiScale = dpiScale;

            if (needsScaling)
            {
                // Calculate the scale to fit in working area
                double scaleX = workingArea.Width / width;
                double scaleY = workingArea.Height / height;
                double scale = Math.Min(scaleX, scaleY);

                scale = Math.Round(scale * 4) / 4.0;

                mainWindow.MinWidth = scale * width;
                mainWindow.MinHeight = scale * height;
                mainWindow.LayoutTransform = new ScaleTransform(scale, scale);

                mainWindow.Width = mainWindow.MinWidth;
                mainWindow.Height = mainWindow.MinHeight;

                TextOptions.SetTextFormattingMode(mainWindow, TextFormattingMode.Display);
                TextOptions.SetTextRenderingMode(mainWindow, TextRenderingMode.ClearType);
                _isCurrentlyScaled = true;
            }
            else if (_isCurrentlyScaled)
            {
                // Reset to normal scale
                mainWindow.LayoutTransform = Transform.Identity;

                mainWindow.MinWidth = width != 0 ? width : mainWindow.MinWidth;
                mainWindow.MinHeight = height != 0 ? height : mainWindow.MinHeight;

                TextOptions.SetTextFormattingMode(mainWindow, TextFormattingMode.Ideal);
                TextOptions.SetTextRenderingMode(mainWindow, TextRenderingMode.Auto);
            }
        }

        public static SerialPort serial = new SerialPort();

        public static void InitSerial(string COM = "")
        {
            if (COM == "" && string.IsNullOrWhiteSpace(Settings.GetSettingString("COMPort")) && GetCOMPortCount() > 0)
            {
                COMSelector cOMSelector = new COMSelector();
                cOMSelector.ShowDialog();
            }
            else if(COM != "")
            {
                var match = Regex.Match(COM, @"COM\d+(?=\))");
                Settings.WriteSetting("COMPort", match.Value);
            }

            if (!serial.IsOpen)
            {
                try
                {
                    var port = Settings.GetSettingString("COMPort");
                    if (!string.IsNullOrWhiteSpace(port))
                    {
                        serial.PortName = port; //Com Port Name                
                        serial.BaudRate = Convert.ToInt32(9600); //COM Port Sp
                        serial.Handshake = System.IO.Ports.Handshake.None;
                        serial.Parity = Parity.None;
                        serial.DataBits = 8;
                        serial.StopBits = StopBits.One;
                        serial.ReadTimeout = 200;
                        serial.WriteTimeout = 50;

                        serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived);
                        serial.Open();
                    }

                }
                catch (Exception ex)
                {
                    MessageBox.Show("Fehler beim Öffnen des COM Ports\nKartenleser kann nicht aktiviert werden\n\nBitte Kartenleser anstecken oder anderen COM Port versuchen", "Fehler beim COM Port", MessageBoxButton.OK, MessageBoxImage.Error);
                    Log.WriteLog(ex.ToString());
                }
            }
        }

        public static int GetCOMPortCount()
        {
            var COMCount = SerialPort.GetPortNames().Count();
            return COMCount;
        }

        public static List<string> GetCOMPorts()
        {
            List<string> coms = new List<string>();

            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Caption like '%(COM%'"))
            {
                var portnames = SerialPort.GetPortNames();
                var ports = searcher.Get().Cast<ManagementBaseObject>().ToList().Select(p => p["Caption"].ToString());

                var portList = portnames.Select(n => n + " - " + ports.FirstOrDefault(s => s.Contains(n))).ToList();

                if (portList.Count > 0)
                {
                    foreach (string s in portList)
                    {
                        if (s.ToLower().Contains("seriell") || s.ToLower().Contains("serial"))
                            coms.Add(s);
                    }
                }
            }

            return coms;
        }

        //public static string ReadSerial()
        //{
        //    if (!serial.IsOpen)
        //        InitSerial();

        //    return Recieve();
        //}

        public static event EventHandler<string> DataReceived;
        public static event EventHandler<byte[]> BinaryDataReceived;
        private static void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (serial.BytesToRead > 0)
                {
                    // Read as string
                    string stringData = serial.ReadExisting();
                    if (!string.IsNullOrWhiteSpace(stringData))
                    {
                        DataReceived?.Invoke(sender, stringData);
                    }

                    // For binary data reception
                    if (serial.BytesToRead > 0)
                    {
                        byte[] binaryData = new byte[serial.BytesToRead];
                        serial.Read(binaryData, 0, binaryData.Length);
                        BinaryDataReceived?.Invoke(sender, binaryData);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error in async data reception: {ex.Message}");
            }
        }

        private static Mutex _mutex;
        internal static void CheckIfMultipleInstances()
        {
            const string mutexName = "ZKuPMutex"; // Eindeutiger Name

            bool createdNew;
            _mutex = new Mutex(true, mutexName, out createdNew);

            if (!createdNew)
            {
                // Bereits eine Instanz aktiv
                var MessageResult = MessageBox.Show("Die Anwendung ist bereits gestartet.\nSoll eine weitere Instanz geöffnet werden?\n\n" +
                    "Ja: Zusätzliche Instanz wird geöffnet\n" +
                    "Nein: Geöffnete Instanz in den Vordergrund\n" +
                    "Abbrechen: Neue Instanz wird beendet", "Hinweis", MessageBoxButton.YesNoCancel, MessageBoxImage.Information);

                switch (MessageResult)
                {
                    case MessageBoxResult.Yes:
                        return;
                    case MessageBoxResult.No:
                        App.BringOtherInstanceToFront();
                        Application.Current.Shutdown();
                        break;
                    case MessageBoxResult.Cancel:
                        Application.Current.Shutdown();
                        break;
                    default:
                        Application.Current.Shutdown();
                        break;
                }

                return;
            }


            //if (Process.GetProcessesByName("ZKuP").Length > 2)
            //{
            //    App.BringOtherInstanceToFront();
            //    Application.Current.Shutdown();
            //}
        }


    

        private static double GetDistanceToWindowEdge(GridSplitter splitter, Window window)
        {
            // Position des Splitters relativ zum Window ermitteln
            System.Windows.Point splitterPosition = splitter.TransformToAncestor(window).Transform(new System.Windows.Point(0, 0));

            // Je nach Orientierung des Splitters
            if (splitter.HorizontalAlignment == HorizontalAlignment.Stretch)
            {
                // Horizontaler Splitter - Abstand zu oberer/unterer Kante
                double topDistance = splitterPosition.Y;
                double bottomDistance = window.ActualHeight - splitterPosition.Y - splitter.ActualHeight;

                // Wenn einer der Abstände negativ ist, ist der Splitter außerhalb
                if (topDistance < 0 || bottomDistance < 0)
                {
                    return Math.Max(topDistance, bottomDistance); // Der weniger negative Wert
                }

                return Math.Min(topDistance, bottomDistance);
            }
            else
            {
                // Vertikaler Splitter - Abstand zu linker/rechter Kante  
                double leftDistance = splitterPosition.X;
                double rightDistance = window.ActualWidth - splitterPosition.X - splitter.ActualWidth;

                // Wenn einer der Abstände negativ ist, ist der Splitter außerhalb
                if (leftDistance < 0 || rightDistance < 0)
                {
                    return Math.Max(leftDistance, rightDistance); // Der weniger negative Wert
                }

                return Math.Min(leftDistance, rightDistance);
            }
        }
    }

    public class ExcelExporter
    {
        private Microsoft.Office.Interop.Excel.Application _excel;
        private Microsoft.Office.Interop.Excel.Workbook _workbook;
        private Microsoft.Office.Interop.Excel.Worksheet _worksheet;
        private Microsoft.Office.Interop.Excel.Range _cellRange;

        public void Export(DataTable table)
        {
            using (var regWord = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("Excel.Application"))
            {
                if (regWord == null)
                {
                    MessageBox.Show("Microsoft Excel wird benötigt, ist aber nicht installiert\nFunktion kann nicht ausgeführt werden", "Excel nicht installiert", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                else
                {
                    _excel = new Microsoft.Office.Interop.Excel.Application
                    {
                        DisplayAlerts = false,
                        Visible = false
                    };

                    _workbook = _excel.Workbooks.Add(Type.Missing);
                    _worksheet = (Microsoft.Office.Interop.Excel.Worksheet)_workbook.ActiveSheet;
                    _worksheet.Columns.NumberFormat = "@";
                    int rowcount = 1;

                    for (int i = 1; i <= table.Columns.Count; i++)
                    {
                        _worksheet.Cells[1, i] = table.Columns[i - 1].ColumnName;
                    }

                    foreach (System.Data.DataRow row in table.Rows)
                    {
                        rowcount += 1;
                        for (int i = 0; i < table.Columns.Count; i++)
                        {
                            _worksheet.Cells[rowcount, i + 1] = row[i].ToString();
                        }
                    }
                    _worksheet.Range["A1:F1"].Interior.Color = Excel.XlRgbColor.rgbLightBlue;


                    _cellRange = _worksheet.Range[_worksheet.Cells[1, 1], _worksheet.Cells[rowcount, table.Columns.Count]];
                    _cellRange.EntireColumn.AutoFit();
                    _excel.Visible = true;
                    //_worksheet.Cells.AutoFilter();
                    //_workbook.SaveAs(pathWithFilename);
                    //_workbook.Close();
                    //_excel.Quit();
                }
            }
        }
    }

    public class ConvertToBackground : System.Windows.Data.IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string input = value.ToString().Substring(0, 2);
            switch (input)
            {
                case "Pr"://üfen!
                    return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightYellow);
                case "OK":
                    return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightGreen);
                case "Fe"://hler!
                    return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightSalmon);
                case "Nu":
                    return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Orange);
                default:
                    return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightGray);
            }

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class ConvertToBackground2 : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string input = value.ToString().Split(' ')[0];
            if (input == "Besucher:")
                return 1;
            else if (input == "Führung:")
                return 2;
            else
                return 0;

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }


    public class ConvertToSlot : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            int input = int.Parse(value.ToString());
            if (input == 1)
                return "14:30 - 16:00";
            else if (input == 2)
                return "16:15 - 17:45";
            else if (input == 3)
                return "18:00 - 19:30";
            else
                return "Error";

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }
    public class HeightMinusConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is double height)
            {
                double subtractValue = 190; // Standard-Wert

                // Optional: Parameter für flexiblen Abzug
                if (parameter != null && double.TryParse(parameter.ToString(), out double paramValue))
                {
                    subtractValue += paramValue;
                }

                double result = height - subtractValue;
                return result > 0 ? result : 0; // Mindestens 0
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    //public class ConvertDateToBackground : System.Windows.Data.IValueConverter
    //{

    //    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    //    {
    //        string input = System.Convert.ToDateTime(value).ToShortDateString();
    //        switch (input)
    //        {
    //            case "01.01.1902"://üfen!
    //                return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);                
    //            default:
    //                return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.White);
    //        }

    //    }

    //    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    //    {
    //        throw new NotImplementedException();
    //    }
    //}


    public class NullToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Wenn value null ist -> sichtbar (Collapsed), sonst Hidden oder Visible
            return string.IsNullOrWhiteSpace(value.ToString()) ? Visibility.Collapsed : Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class IntToCategory : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string input = value as string;
            switch (input)
            {
                case "1":
                    return "Fremdfirma";
                case "2":
                    return "Besucher";
                case "3":
                    return "Lieferant";
                case "4":
                    return "Notdienst";
                case "5":
                    return "Führung";
                default:
                    return "Fehler!";
            }

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class IntToGenehmigungsstatus : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string input = value.ToString();
            switch (input)
            {
                case "0":
                    return "Unbearbeitet";
                case "1":
                    return "Genehmigt";
                case "2":
                    return "Abgelehnt";
                case "3":
                    return "Genehmigt\nMail gesendet";
                case "4":
                    return "Abgelehnt\nMail gesendet";
                default:
                    return "Fehler!";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class BoolToCheckBox : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int input = value != null ? System.Convert.ToInt16(value) : 0;

            switch (input)
            {
                case 1:
                    return true;
                case 0:
                    return false;
                default:
                    return false;
            }

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool input = System.Convert.ToBoolean(value);
            switch (input)
            {
                case true:
                    return 1;
                case false:
                    return 0;
                default:
                    return 0;
            }
        }
    }

    public class IntToBool : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int input = value != null ? System.Convert.ToInt16(value) : 0;

            switch (input)
            {
                case 1:
                    return true;
                case 0:
                    return false;
                default:
                    return false;
            }

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool input = System.Convert.ToBoolean(value);
            switch (input)
            {
                case true:
                    return 1;
                case false:
                    return 0;
                default:
                    return 0;
            }
        }
    }

    public class IntToYesNo : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int input = value != null ? System.Convert.ToInt16(value) : 0;

            switch (input)
            {
                case 1:
                    return "Ja";
                case 0:
                    return "Nein";
                default:
                    return "Nein";
            }

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //throw new NotImplementedException();
            return 0;
        }
    }

    public class MultiToBackground : System.Windows.Data.IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            try
            {
                if (System.Convert.ToInt16(values[0]) <= System.Convert.ToInt16(values[1]))
                    return new SolidColorBrush(Colors.LightGreen);
                else return new SolidColorBrush(Colors.LightYellow);

            }
            catch (Exception)
            {
                return new SolidColorBrush(Colors.LightYellow);
            }
        }

        public object[] ConvertBack(object values, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class DBNullToBool : System.Windows.Data.IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null || value == DBNull.Value)
                return false;
            else return true;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

    public class IntToRole : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //string[] roles = { "Pförtner", "FFK", "Admin", "FFK Sasse", "M2" };
            
            try
            {
                return ManageUsers.RoleList[(int)value];
                //return roles[(int)value];
            }
            catch (Exception)
            {
                return "Error";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
    public class ComboBoxDisplayConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is DataRowView row)
            {
                return $"{row["Name"]} - {row["Beauftragte_Leistung"]}";
            }
            return value?.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    [ValueConversion(typeof(string), typeof(BitmapSource))]
    public class SystemIconConverter : IValueConverter
    {
        public object Convert(object value, Type type, object parameter, System.Globalization.CultureInfo culture)
        {
            System.Drawing.Icon icon = (System.Drawing.Icon)typeof(System.Drawing.SystemIcons).GetProperty(parameter.ToString(), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetValue(null, null);
            BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            return bs;
        }

        public object ConvertBack(object value, Type type, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

    public class DatePickerHelper
    {
        public static readonly DependencyProperty ShowTodayButtonProperty = DependencyProperty.RegisterAttached("ShowTodayButton", typeof(bool), typeof(DatePickerHelper), new FrameworkPropertyMetadata(false, ShowTodayButtonChanged));
        public static readonly DependencyProperty ShowTodayButtonContentProperty = DependencyProperty.RegisterAttached("ShowTodayButtonContent", typeof(string), typeof(DatePickerHelper), new FrameworkPropertyMetadata("Today", ShowTodayButtonContentChanged));

        private static void ShowTodayButtonContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Call twice to update Button Content if was setted after ShowTodayButton
            SetShowTodayButton(d, !GetShowTodayButton(d));
            SetShowTodayButton(d, !GetShowTodayButton(d));
        }

        private static void ShowTodayButtonChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (sender is DatePicker)
            {
                var d = (DatePicker)sender;
                var showButton = (bool)(e.NewValue);

                if (showButton)
                {
                    //I want to reproduce this xaml in c#:
                    //<Style TargetType="{x:Type Calendar}">
                    //    <Setter Property="Template">
                    //        <Setter.Value>
                    //            <ControlTemplate TargetType="{x:Type Calendar}">
                    //                <StackPanel Name="PART_Root"
                    //                            HorizontalAlignment="Center">
                    //                    <CalendarItem Name="PART_CalendarItem"
                    //                                    Background="{TemplateBinding Control.Background}"
                    //                                    BorderBrush="{TemplateBinding Control.BorderBrush}"
                    //                                    BorderThickness="{TemplateBinding Control.BorderThickness}"
                    //                                    Style="{TemplateBinding Calendar.CalendarItemStyle}" />
                    //                    <Button Command="SelectToday"
                    //                            Content="Today" />
                    //                </StackPanel>
                    //            </ControlTemplate>
                    //        </Setter.Value>
                    //    </Setter>
                    //</Style>

                    Setter setter = new Setter();
                    setter.Property = Calendar.TemplateProperty;
                    ControlTemplate template = new ControlTemplate(typeof(Calendar));
                    var stackPanel = new FrameworkElementFactory(typeof(StackPanel));
                    stackPanel.Name = "PART_Root";
                    stackPanel.SetValue(StackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Center);

                    var calendar = new FrameworkElementFactory(typeof(CalendarItem));
                    calendar.Name = "PART_CalendarItem";

                    calendar.SetBinding(CalendarItem.BackgroundProperty,
                        new Binding(CalendarItem.BackgroundProperty.Name)
                        {
                            Path = new PropertyPath(Control.BackgroundProperty),
                            RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent)
                        });

                    calendar.SetBinding(CalendarItem.BorderBrushProperty, new Binding(CalendarItem.BorderBrushProperty.Name)
                    {
                        Path = new PropertyPath(Control.BorderBrushProperty),
                        RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent)
                    });

                    calendar.SetBinding(CalendarItem.BorderThicknessProperty, new Binding(CalendarItem.BorderThicknessProperty.Name)
                    {
                        Path = new PropertyPath(Control.BorderThicknessProperty),
                        RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent)
                    });

                    calendar.SetBinding(CalendarItem.StyleProperty, new Binding(CalendarItem.StyleProperty.Name)
                    {
                        Path = new PropertyPath(Calendar.CalendarItemStyleProperty),
                        RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent)
                    });

                    stackPanel.AppendChild(calendar);

                    var btn = new FrameworkElementFactory(typeof(Button));
                    btn.SetValue(Button.ContentProperty, GetShowTodayButtonContent(d));

                    var SelectToday = new RoutedCommand("Today", typeof(DatePickerHelper));

                    d.CommandBindings.Add(new CommandBinding(SelectToday,
              (s, ea) =>
              {
                  if (!(s as DatePicker).BlackoutDates.Contains(DateTime.Now.Date))
                  {
                      (s as DatePicker).SelectedDate = DateTime.Now.Date;
                      (s as DatePicker).IsDropDownOpen = false;
                  }
                  else
                      MessageBox.Show("Das angegebene Datum ist blockiert", "Auswahl nicht möglich", MessageBoxButton.OK, MessageBoxImage.Stop);
              },
              (s, ea) => { ea.CanExecute = true; }));

                    btn.SetValue(Button.CommandProperty, SelectToday);

                    stackPanel.AppendChild(btn);

                    template.VisualTree = stackPanel;
                    setter.Value = template;

                    Style Temp = new Style(typeof(Calendar));

                    Temp.Setters.Add(setter);

                    d.CalendarStyle = Temp;
                }
            }
        }

        [AttachedPropertyBrowsableForType(typeof(DatePicker))]
        public static bool GetShowTodayButton(DependencyObject obj)
        {
            return (bool)obj.GetValue(ShowTodayButtonProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(DatePicker))]
        public static void SetShowTodayButton(DependencyObject obj, bool value)
        {
            obj.SetValue(ShowTodayButtonProperty, value);
        }

        [AttachedPropertyBrowsableForType(typeof(DatePicker))]
        public static string GetShowTodayButtonContent(Control obj)
        {
            return (string)obj.GetValue(ShowTodayButtonContentProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(DatePicker))]
        public static void SetShowTodayButtonContent(Control obj, string value)
        {
            obj.SetValue(ShowTodayButtonContentProperty, value);
        }
    }

    public class PopupNonTopmost : Popup
    {
        public static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner(
            typeof(PopupNonTopmost),
            new FrameworkPropertyMetadata(false, OnTopmostChanged));

        public bool Topmost
        {
            get { return (bool)GetValue(TopmostProperty); }
            set { SetValue(TopmostProperty, value); }
        }

        private static void OnTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            (obj as PopupNonTopmost).UpdateWindow();
        }

        protected override void OnOpened(EventArgs e)
        {
            UpdateWindow();
        }

        private void UpdateWindow()
        {
            var hwnd = ((HwndSource)PresentationSource.FromVisual(this.Child)).Handle;
            RECT rect;

            if (GetWindowRect(hwnd, out rect))
            {
                SetWindowPos(hwnd, Topmost ? -1 : -2, rect.Left, rect.Top, (int)this.Width, (int)this.Height, 0);
            }
        }

        #region P/Invoke imports & definitions

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32", EntryPoint = "SetWindowPos")]
        private static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags);

        #endregion
    }
}
