using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Basics.UserControls.AdaptableIcon;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;

using static C4IT.Logging.cLogManager;


namespace FasdDesktopUi.Basics
{
    public static class cUtility
    {
        public static readonly IRawValueFormatter RawValueFormatter = new RawValueFormatter();

        public static int SmoothedInt(int oldValue, int newValue)
        {
            if (oldValue < newValue)
            {
                return newValue - oldValue < 20 ? newValue : oldValue + 20;
            }
            else if (oldValue > newValue)
            {
                double flattenValue = (oldValue - newValue) / 7.0;
                return Convert.ToInt32(oldValue - Math.Ceiling(flattenValue));
            }
            else
            {
                return oldValue;
            }
        }

        public static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            const int WM_GETMINMAXINFO = 0x0024;

            switch (msg)
            {
                case WM_GETMINMAXINFO:
                    WmGetMinMaxInfo(hwnd, lParam, 300, 200);
                    handled = true;
                    break;
            }
            return IntPtr.Zero;
        }

        public static async Task ChangeIconToCheckAsync(AdaptableIcon copyIcon, int transitionTimeInMilliSeconds = 500)
        {
            try
            {
                if (copyIcon.SelectedInternIcon == enumInternIcons.misc_check)
                    return;

                var currentPrimaryIconColorValue = copyIcon.ReadLocalValue(AdaptableIcon.PrimaryIconColorProperty);
                var tempSelectedIcon = copyIcon.GetCurrentIcon();

                copyIcon.SetIcon((enumInternIcons.misc_check, null, null, null));
                copyIcon.PrimaryIconColor = (SolidColorBrush)Application.Current.FindResource("Color.Green");

                await Task.Delay(transitionTimeInMilliSeconds);

                copyIcon.SetIcon(tempSelectedIcon);
                copyIcon.SetValue(AdaptableIcon.PrimaryIconColorProperty, currentPrimaryIconColorValue);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #region SetTickerTextAnimation

        public static void SetTickerTextAnimation(FrameworkElement TickerElement, FrameworkElement ParentElement)
        {
            try
            {
                const double marginPerSecond = 60.0;
                const double pauseDurationInSeconds = 0.5;

                ParentElement.Width = ParentElement.ActualWidth;

                if (TickerElement is TextBlock TickerTextBlock)
                    TickerTextBlock.TextTrimming = TextTrimming.None;

                TickerElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                var resultDesiredSize = TickerElement.DesiredSize;

                var missingSpace = resultDesiredSize.Width - ParentElement.ActualWidth;

                if (missingSpace <= 0)
                    return;


                var animDurationInSeconds = missingSpace / marginPerSecond + pauseDurationInSeconds;

                ThicknessAnimationUsingKeyFrames thicknessAnimationUsingKeyFrames = new ThicknessAnimationUsingKeyFrames() { RepeatBehavior = RepeatBehavior.Forever, AutoReverse = true };
                _ = thicknessAnimationUsingKeyFrames.KeyFrames.Add(new LinearThicknessKeyFrame(new Thickness(0), KeyTime.FromTimeSpan(TimeSpan.FromSeconds(pauseDurationInSeconds))));
                _ = thicknessAnimationUsingKeyFrames.KeyFrames.Add(new LinearThicknessKeyFrame(new Thickness(-missingSpace, 0, 0, 0), KeyTime.FromTimeSpan(TimeSpan.FromSeconds(animDurationInSeconds))));
                _ = thicknessAnimationUsingKeyFrames.KeyFrames.Add(new LinearThicknessKeyFrame(new Thickness(-missingSpace, 0, 0, 0), KeyTime.FromTimeSpan(TimeSpan.FromSeconds(animDurationInSeconds + pauseDurationInSeconds))));

                TickerElement.BeginAnimation(FrameworkElement.MarginProperty, thicknessAnimationUsingKeyFrames);
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
            }
        }

        #endregion

        #region SizeRegulation

        public static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam, int MinX, int MinY)
        {
            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
            int MONITOR_DEFAULTTONEAREST = 0x00000002;
            IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
            if (monitor != IntPtr.Zero)
            {
                MONITORINFO monitorInfo = new MONITORINFO();
                GetMonitorInfo(monitor, monitorInfo);
                RECT rcWorkArea = monitorInfo.rcWork;
                RECT rcMonitorArea = monitorInfo.rcMonitor;
                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
                mmi.ptMinTrackSize.x = MinX;
                mmi.ptMinTrackSize.y = MinY;
                mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x;
                mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;
            }
            Marshal.StructureToPtr(mmi, lParam, true);
        }


        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            /// <summary>x coordinate of point.</summary>
            public int x;
            /// <summary>y coordinate of point.</summary>
            public int y;
            /// <summary>Construct a point of coordinates (x,y).</summary>
            public POINT(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MINMAXINFO
        {
            public POINT ptReserved;
            public POINT ptMaxSize;
            public POINT ptMaxPosition;
            public POINT ptMinTrackSize;
            public POINT ptMaxTrackSize;
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class MONITORINFO
        {
            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
            public RECT rcMonitor = new RECT();
            public RECT rcWork = new RECT();
            public int dwFlags = 0;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
            public static readonly RECT Empty = new RECT();
            public int Width { get { return Math.Abs(right - left); } }
            public int Height { get { return bottom - top; } }
            public RECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }
            public RECT(RECT rcSrc)
            {
                left = rcSrc.left;
                top = rcSrc.top;
                right = rcSrc.right;
                bottom = rcSrc.bottom;
            }
            public bool IsEmpty { get { return left >= right || top >= bottom; } }
            public override string ToString()
            {
                if (this == Empty) { return "RECT {Empty}"; }
                return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
            }
            public override bool Equals(object obj)
            {
                if (!(obj is Rect)) { return false; }
                return (this == (RECT)obj);
            }
            /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
            public override int GetHashCode() => left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
            /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
            public static bool operator ==(RECT rect1, RECT rect2) { return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom); }
            /// <summary> Determine if 2 RECT are different(deep compare)</summary>
            public static bool operator !=(RECT rect1, RECT rect2) { return !(rect1 == rect2); }
        }

        [DllImport("user32")]
        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

        [DllImport("User32")]
        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);


        #endregion


        public static System.Windows.Size GetDpiFactor()
        {
            using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
            {
                double dpiX = graphics.DpiX / 96;
                double dpiY = graphics.DpiY / 96;
                dpiX = dpiX == 0 ? 1.0 : dpiX;
                dpiY = dpiY == 0 ? 1.0 : dpiY;

                var result = new Size { Width = dpiX, Height = dpiY };
                return result;
            }
        }

        #region SetCurrentTextToClipboard

        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

        public static void BringWindowToFront(Window w)
        {
            var hWnd = new WindowInteropHelper(w).Handle;
            SetForegroundWindow(hWnd);
            w.Focus();
        }

        public static void SendCtrlCToCurrentWindow()
        {
            var hWnd = GetForegroundWindow();

            uint KEYEVENTF_KEYUP = 2;
            byte VK_CONTROL = 0x11;
            SetForegroundWindow(hWnd);
            keybd_event(VK_CONTROL, 0, 0, 0);
            keybd_event(0x43, 0, 0, 0); //Send the C key (43 is "C")
            keybd_event(0x43, 0, KEYEVENTF_KEYUP, 0);
            keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);// 'Left Control Up
        }

        #endregion

        #region String Manipulation

        public static string CleanPhoneString(string input)
        {
            string output = input;

            try
            {

                if (input.Any(inputChar => char.IsLetter(inputChar)))
                    return output;

                output = input.Replace(" ", "");

                if (output.StartsWith("+"))
                    output = output.Remove(0, 3);

                output = new string(output.Where(inputChar => char.IsDigit(inputChar)).ToArray());

            }
            catch (Exception E)
            {
                LogException(E);
            }

            return output;
        }

        #endregion

        #region GetQuickActionCopyStrings

        internal static bool ShouldHideQuickActionOutput(string outputValueKey, cFasdQuickAction quickActionDefinition)
        {
            try
            {
                if (quickActionDefinition.ColumnOutputFormattings is null)
                    return false;

                if (!quickActionDefinition.ShowAllOutputContent && !quickActionDefinition.ColumnOutputFormattings.ContainsKey(outputValueKey))
                    return true;

                if (quickActionDefinition.ColumnOutputFormattings.TryGetValue(outputValueKey, out var columnFormatting))
                    return columnFormatting.Hidden;
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            return false;
        }

        public static string GetHtmlFrame(string Content)
        {
            var strLen1 = (Content.Length + 142).ToString("D10");
            var strLen2 = (Content.Length + 173).ToString("D10");

            var RetVal = "Version:0.9\n";
            RetVal += "StartHTML:0000000105\n";
            RetVal += "EndHTML:" + strLen2 + "\n";
            RetVal += "StartFragment:0000000139\n";
            RetVal += "EndFragment:" + strLen1 + "\n";
            RetVal += "<html><body><!--StartFragment-->\n";
            RetVal += Content + "\n";
            RetVal += "<!--EndFragment--></body></html>";

            return RetVal;
        }

        #endregion

        #region ToolTipHandling
        public static void FillTresholdToolTip(cStateThresholdValues _v, StatusTreshholdTooltip _t)
        {
            try
            {
                FormattingOptions options = new FormattingOptions() { ReferenceDate = DateTime.UtcNow.AddDays(_v.ReferenceDays), TimeZone = TimeZoneInfo.Local };
                RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
                RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);

                var _strVal = RawValueFormatter.GetDisplayValue(_v.Value, _v.StateDefinition.DisplayType, options);
                _t.rawValue.Text = _strVal;

                if (_v.StateDefinition is cHealthCardStateLevel _sl)
                {
                    _strVal = RawValueFormatter.GetDisplayValue(_sl.Warning, _v.StateDefinition.DisplayType, options);
                    _t.tresholdWarning.Text = _strVal;
                    _strVal = RawValueFormatter.GetDisplayValue(_sl.Error, _v.StateDefinition.DisplayType, options);
                    _t.tresholdError.Text = _strVal;
                }
                else if (_v.StateDefinition is cHealthCardStateVersion _sv)
                {
                    _strVal = RawValueFormatter.GetDisplayValue(_sv.Warning, _v.StateDefinition.DisplayType, options);
                    _t.tresholdWarning.Text = _strVal;
                    _strVal = RawValueFormatter.GetDisplayValue(_sv.Error, _v.StateDefinition.DisplayType, options);
                    _t.tresholdError.Text = _strVal;
                }
                else if (_v.StateDefinition is cHealthCardStateDateTime _sd)
                {

                    _strVal = RawValueFormatter.GetDisplayValue(_sd.WarningHours, RawValueType.DURATION_HOUR, options);
                    _t.tresholdWarning.Text = _strVal;
                    _strVal = RawValueFormatter.GetDisplayValue(_sd.ErrorHours, RawValueType.DURATION_HOUR, options);
                    _t.tresholdError.Text = _strVal;
                }
                else if (_v.StateDefinition is cHealthCardStateTranslation _st)
                {
                    var _trans = cF4SDHealthCardConfig.GetTranslationsWithName(_st, _st.Translation);
                    var strVal = _v.Value.ToString();
                    if (_trans is cHealthCardTranslator _trans2)
                    {
                        _strVal = null;
                        cHealthCardTranslator.cEntry tEntry = null;
                        foreach (var _trans3 in _trans2.Translations)
                        {
                            if (_trans3.Values.Any(v => v.Equals(strVal, StringComparison.InvariantCultureIgnoreCase)))
                            {
                                tEntry = _trans3;
                                break;
                            }
                        }
                        if (tEntry == null)
                        {
                            tEntry = _trans2.DefaultTranslation;
                            _strVal = _trans2.DefaultTranslation.Translation.GetValue(_v.Value.ToString());
                        }

                        if (tEntry == null)
                            _strVal = _v.Value.ToString();
                        else
                            _strVal = tEntry.Translation.GetValue(Default: _v.Value.ToString());

                        _t.rawValue.Text = _strVal;

                        var _Warning = "";
                        foreach (var trEntry in _trans2.Translations.Where(v => v.StateLevel == enumHealthCardStateLevel.Warning))
                        {
                            if (_Warning != "")
                                _Warning += " ";
                            _Warning += trEntry.Translation.GetValue();
                        }

                        var _Error = "";
                        foreach (var trEntry in _trans2.Translations.Where(v => v.StateLevel == enumHealthCardStateLevel.Error))
                        {
                            if (_Error != "")
                                _Error += " ";
                            _Error += trEntry.Translation.GetValue();
                        }

                        if (_trans2.DefaultTranslation.StateLevel == enumHealthCardStateLevel.Warning)
                        {
                            if (_Warning != "")
                                _Warning += " ";
                            _Warning += _trans2.DefaultTranslation.Translation.GetValue();
                        }

                        if (_trans2.DefaultTranslation.StateLevel == enumHealthCardStateLevel.Error)
                        {
                            if (_Error != "")
                                _Error += " ";
                            _Error += _trans2.DefaultTranslation.Translation.GetValue();
                        }

                        _t.tresholdWarning.Text = _Warning;
                        _t.tresholdError.Text = _Error;

                    }
                }
                else
                {
                    _t.tresholdWarning.Text = "";
                    _t.tresholdError.Text = "";
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #endregion

    }
}
