using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ChatController.Core;
using RestSharp;
using MessageBox = System.Windows.MessageBox;
using PixelFormat = System.Drawing.Imaging.PixelFormat;

namespace ChatController.Utilities
{
    public class Utils
    {
        public static string AuthToken { get; set; }
        
        public static string Tenant { get; set; }

        public static DateTime DefaultDate = new DateTime(1, 1, 1);

        public static void DownloadGroupProfilePictureAsync(string pathToProfilePicture, string key, Action<ImageSource> callback)
        {
            var cache = OwnChatCache.GetInstance();

            var cachedProfileImage = cache.GetCachedProfilePicture(key, pathToProfilePicture);

            if (!(cachedProfileImage is null))
            {
                callback?.Invoke(cachedProfileImage);
                return;
            }

            DownloadImage(pathToProfilePicture, imageSource =>
            {
                cache.AddProfilePictureToCache(key, imageSource, pathToProfilePicture);
                callback?.Invoke(imageSource);
            });
        }

        public static void DownloadUserProfilePicturesAsync(Dictionary<long, string> userIds2Uris)
        {
            var cache = OwnChatCache.GetInstance();

            foreach(var userId2Uri in userIds2Uris)
            {
                if(cache.IsUserProfilePictureInCache(userId2Uri.Key))
                {
                    continue;
                }

                DownloadImage(userId2Uri.Value, imageSource =>
                {
                    cache.AddUserProfilePictureToCache(imageSource, userId2Uri.Key, userId2Uri.Value);
                });
            }
        }

        private static void DownloadImage(string uri, Action<ImageSource> callback)
        {
            var client = new RestClient(uri);
            var request = new RestRequest
            {
                ResponseWriter = stream =>
                {
                    var rotation = Rotation.Rotate0;

                    using(var image = Image.FromStream(stream))
                    {
                        if(image.PropertyIdList.Contains(0x112))
                        {
                            rotation = GetRotationFromExifTag(image.GetPropertyItem(0x112).Value[0]);
                        }

                        var bitmap = new BitmapImage();

                        var localStream = new MemoryStream();

                        image.Save(localStream, image.RawFormat);

                        localStream.Position = 0;

                        bitmap.BeginInit();
                        bitmap.StreamSource = localStream;
                        bitmap.Rotation = rotation;
                        bitmap.EndInit();
                        bitmap.Freeze();

                        callback?.Invoke(bitmap);
                    }
                }
            };

            request.AddHeader(Constants.Token, AuthToken);
            request.AddHeader(Constants.CustomerId, Tenant);

            client.ExecuteAsync(request, null);
        }

        private static Rotation GetRotationFromExifTag(int value)
        {
            switch (value)
            {
                case 6:
                    return Rotation.Rotate90;
                case 8:
                    return Rotation.Rotate270;
                case 3:
                    return Rotation.Rotate180;
                default:
                    return Rotation.Rotate0;
            }
        }

        public static void DownloadImageAsync(string uri, string key, CacheCategory cacheCategory, Action<ImageSource> callback)
        {
            if(string.IsNullOrEmpty(uri))
            {
                callback?.Invoke(null);
                return;
            }
            
            var cache = OwnChatCache.GetInstance();

            var cachedImage = cache.GetImageSourceFromCache(key, uri, cacheCategory);

            if (cachedImage != null)
            {
                callback?.Invoke(cachedImage);
                return;
            }

            DownloadImage(uri, imageSource =>
            {
                cache.AddImageToCache(key, imageSource, cacheCategory, uri);
                callback?.Invoke(imageSource);
            });
        }

        private static ImageSource GetDefaultImageSource(bool pIsGroup, bool pIsEmployee)
        {
            var defaultImage = Constants.EmployeeDefaultImagePath;

            if (pIsGroup)
            {
                defaultImage = pIsEmployee ? Constants.TeamDefaultImagePath : Constants.NormalGroupDefaultImagePath;
            }
            else if (!pIsEmployee)
            {
                defaultImage = Constants.CustomerDefaultImagePath;
            }

            var resultBitmapImage = new BitmapImage();

            resultBitmapImage.BeginInit();
            resultBitmapImage.UriSource = new Uri(defaultImage);
            resultBitmapImage.EndInit();
            resultBitmapImage.Freeze();

            return resultBitmapImage;
        }

        public static ImageSource GetPicturePlaceholder()
        {
            var resultBitmapImage = new BitmapImage();

            resultBitmapImage.BeginInit();
            resultBitmapImage.UriSource = new Uri(Constants.ImagePlaceholderPath);
            resultBitmapImage.EndInit();
            resultBitmapImage.Freeze();

            return resultBitmapImage;
        }

        public static string ReadStream(WebResponse response)
        {
            var dataStream = response.GetResponseStream();

            if (dataStream == null)
            {
                return null;
            }

            var reader = new StreamReader(dataStream);
            var responseFromServer = reader.ReadToEnd();

            dataStream.Dispose();
            reader.Dispose();

            dataStream.Close();
            reader.Close();

            return responseFromServer;
        }

        public static string GetAndCreateUserAppDataPath()
        {
            try
            {
                var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                var companyFilePath = Path.Combine(localAppData, Resource.CompanyName);

                if (!Directory.Exists(companyFilePath))
                {
                    Directory.CreateDirectory(companyFilePath);
                }

                var bewoFilePath = Path.Combine(companyFilePath, Resource.ApplicationFolderName);

                if (!Directory.Exists(bewoFilePath))
                {
                    Directory.CreateDirectory(bewoFilePath);
                }

                return bewoFilePath;
            }
            catch (Exception exception)
            {
                MessageBox.Show("Fehler beim Anlegen des Anwendungsordners. Sie besitzen nicht die erforderlichen Rechte, bitte wenden Sie sich an Ihren Systemadministrator.\n" + exception.Message, "BeWoPlaner", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }

            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        }

        public static string GenerateTempName()
        {
            const int length = 6;
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            var random = new Random();

            var tempName = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());

            return tempName;
        }

        public static byte[] ImageToByteArray(Image pImage)
        {
            try
            {
                using (var memoryStream = new MemoryStream())
                {
                    pImage.Save(memoryStream, ImageFormat.Bmp);
                    return memoryStream.ToArray();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("Fehler: " + e.Message, "Fehler", MessageBoxButton.OK);
                return null;
            }
        }

        public static ImageSource ConvertIconToImageSource(Icon pIcon)
        {
            var bitmap = new Bitmap(pIcon.Width, pIcon.Height);
            
            var graphics = Graphics.FromImage(bitmap);

            graphics.DrawIcon(pIcon, 0, 0);

            graphics.Dispose();

            bitmap.Save("icon.ico", ImageFormat.Icon);

            var imageSource = ImageSourceForBitmap(bitmap);
            
            bitmap.Dispose();

            return imageSource;
        }

        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DeleteObject([In] IntPtr hObject);

        public static ImageSource ImageSourceForBitmap(Bitmap bmp)
        {
            var handle = bmp.GetHbitmap();

            try
            {
                return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(handle);
            }
        }

        public static Icon ImageSourceToIcon(ImageSource pImageSource)
        {
            var bitmapSource = (BitmapSource) pImageSource;

            var width = bitmapSource.PixelWidth;
            var height = bitmapSource.PixelHeight;

            var newWidth = width;
            var newHeight = height;

            var x = 0;
            var y = 0;

            if(width < height)
            {
                newHeight = width;
                y = (height - width) / 2;
            }
            else
            {
                newWidth = height;
                x = (width - height) / 2;
            }

            var stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8);
            var memoryBlockPointer = Marshal.AllocHGlobal(height * stride);

            bitmapSource.CopyPixels(new Int32Rect(x, y, newWidth, newHeight), memoryBlockPointer, newHeight * stride, stride);
            
            var bitmap = new Bitmap(newWidth, newHeight, stride, PixelFormat.Format32bppPArgb, memoryBlockPointer);

            return Icon.FromHandle(bitmap.GetHicon());
        }

        public static byte[] ReadFully(Stream pStream)
        {
            using (var memStream = new MemoryStream())
            {
                pStream.CopyTo(memStream);
                return memStream.ToArray();
            }
        }

        public static RotateFlipType OrientationToFlipType(string orientation)
        {
            switch (int.Parse(orientation))
            {
                case 1:
                    return RotateFlipType.RotateNoneFlipNone;
                case 2:
                    return RotateFlipType.RotateNoneFlipX;
                case 3:
                    return RotateFlipType.Rotate180FlipNone;
                case 4:
                    return RotateFlipType.Rotate180FlipX;
                case 5:
                    return RotateFlipType.Rotate90FlipX;
                case 6:
                    return RotateFlipType.Rotate90FlipNone;
                case 7:
                    return RotateFlipType.Rotate270FlipX;
                case 8:
                    return RotateFlipType.Rotate270FlipNone;
                default:
                    return RotateFlipType.RotateNoneFlipNone;
            }
        }

        public static List<T> GetVisualChildCollection<T>(object parent) where T : Visual
        {
            var visualCollection = new List<T>();
            GetVisualChildCollection(parent as DependencyObject, visualCollection);

            return visualCollection;
        }

        public static void GetVisualChildCollection<T>(DependencyObject parent, ICollection<T> visualCollection) where T : Visual
        {
            var count = VisualTreeHelper.GetChildrenCount(parent);
            for(var i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);

                if(child is T item)
                {
                    visualCollection.Add(item);
                }
                else
                {
                    GetVisualChildCollection(child, visualCollection);
                }
            }
        }

        /*
         * try
                    {
                        using (var form = new Form())
                        {
                            using (var ms = new MemoryStream(e.Result))
                            {
                                var image = System.Drawing.Image.FromStream(ms);
                                
                                using (var bitmap = new Bitmap(image))
                                {
                                    short orient = 0;
                                    const int orientationId = 0x0112;

                                    if (image.PropertyIdList.Contains(orientationId))
                                    {
                                        var item = image.GetPropertyItem(orientationId);

                                        orient = BitConverter.ToInt16(item.Value, 0);

                                        bitmap.SetPropertyItem(item);
                                    }

                                    var flipType = Utils.OrientationToFlipType(orient.ToString());

                                    bitmap.RotateFlip(flipType);

                                    form.StartPosition = FormStartPosition.CenterScreen;
                                    form.ClientSize = bitmap.Size;
                                    form.FormBorderStyle = FormBorderStyle.Sizable;
                                    form.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
                                        
                                    using (var pictureBox = new PictureBox())
                                    {
                                        pictureBox.Dock = DockStyle.Fill;
                                        pictureBox.Image = bitmap;

                                        pictureBox.SizeMode = PictureBoxSizeMode.Zoom;

                                        form.Controls.Add(pictureBox);

                                        downloadCompletedCallback?.Invoke();

                                        form.ShowDialog();
                                    }
                                }
                            }
                        }
         */

        public static int GetHeightFromThumbnailUri(string uri)
        {
            /*
                https://test.ownchat.de/document.png
                https://test.ownchat.de/profile/view-image/{customerid}/{width}/{height}/{filename}
                https://test.ownchat.de/message/view-image/{customerid}/{group}/{height}/{filename}
             */

            if (uri != null && uri.Contains("/"))
            {
                var splitUri = uri.Split('/');

                if(splitUri.Length>=2)
                {
                    var heightStr = splitUri[splitUri.Length - 2];

                    if(int.TryParse(heightStr, out var height))
                    {
                        return height;
                    }
                }
            }

            return 100;
        }

        private static readonly Regex _UrlRegex = new Regex(@"(?#Protocol)(http(?:s?)\:(\/\/|\\\\)|(w){3}(2|3)?\.{1})(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&amp;(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);

        public static ObservableCollection<Inline> ConvertLinksToHyperlinks(string messageText)
        {
            var result = new ObservableCollection<Inline>();

            if(messageText == null)
            {
                return result;
            }

            var guidString = Guid.NewGuid().ToString();
            var replacementString = Guid.NewGuid().ToString();

            var matches = _UrlRegex.Matches(messageText);

            var index2Link = new Dictionary<int, Hyperlink>();

            for(var i = 0; i < matches.Count; i++)
            {
                var link = matches[i].Value;

                if(!link.Contains("http"))
                {
                    link = $"http://{link}";
                }

                var hyperlink = new Hyperlink(new Run(link))
                {
                    NavigateUri = new Uri(link, UriKind.Absolute)
                };

                hyperlink.RequestNavigate += (s, e) =>
                {
                    Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
                    e.Handled = true;
                };

                index2Link[i] = hyperlink;
            }

            var splitMessage = _UrlRegex.Replace(messageText, replacementString + guidString).Split(new[] {guidString}, StringSplitOptions.None);

            var linkIndex = 0;
            foreach(var text in splitMessage)
            {
                if(text.Equals(replacementString))
                {
                    result.Add(index2Link[linkIndex]);
                    linkIndex++;
                }
                else if(text.StartsWith(replacementString) || text.EndsWith(replacementString))
                {
                    var splitLine = text.Split(new[] { replacementString }, StringSplitOptions.None);

                    foreach(var partLine in splitLine)
                    {
                        if(string.Empty.Equals(partLine))
                        {
                            result.Add(index2Link[linkIndex]);
                            linkIndex++;
                        }
                        else
                        {
                            result.Add(new Run(partLine));
                        }
                    }
                }
                else
                {
                    result.Add(new Run(text));
                }
            }

            return result;
        }
    }
}
