using InABox.Core;
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using InABox.WPF;
using Color = System.Drawing.Color;
using Image = System.Windows.Controls.Image;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.Win32;
using Syncfusion.UI.Xaml.ImageEditor;
using InABox.Wpf;

namespace InABox.DynamicGrid
{
    public static class EmbeddedImageUtilities
    {
        private static BitmapImage? EmptyImage;
        private static BitmapImage? EmptySignature;
        public static ImageSource CreateEmptyImage()
        {
            EmptyImage ??= new Bitmap(256, 256).WatermarkImage("No Image", Color.Gray).AsBitmapImage();
            return EmptyImage;
        }
        
        public static ImageSource CreateEmptySignature()
        {
            EmptySignature ??= new Bitmap(400, 400).WatermarkImage("No Signature", Color.Gray).AsBitmapImage();
            return EmptySignature;
        }

        public static bool SelectImageFile([NotNullWhen(true)] out byte[]? data)
        {
            var dlg = new OpenFileDialog();
            dlg.Filter = "Image Files (*.jpg;*.png;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";
            if (dlg.ShowDialog() == true)
            {
                var filename = Path.GetFileName(dlg.FileName).ToLower();
                var timestamp = new FileInfo(dlg.FileName).LastWriteTime;
                data = File.ReadAllBytes(dlg.FileName);
                return true;
            }
            data = null;
            return false;
        }
        
        public static byte[]? SaveImageToBytes(Image? image, bool isEmpty, BitmapEncoder encoder)
        {
            if (image == null
                || image.Source is not BitmapImage bmpimg) return null;
            if (isEmpty) return null;

            byte[] data;
            encoder.Frames.Add(BitmapFrame.Create(bmpimg));
            using (var ms = new MemoryStream())
            {
                encoder.Save(ms);
                ms.Seek(0, SeekOrigin.Begin);
                data = ms.ToArray();
            }

            return data;
        }
        
        public static string SaveImageToString(Image? image, bool isEmpty, BitmapEncoder encoder)
        {
            var data = SaveImageToBytes(image, isEmpty, encoder);
            return data != null ? Convert.ToBase64String(data) : "";
        }
        
        /// <summary>
        /// Loads a value into an Image object.
        /// </summary>
        /// <param name="image"></param>
        /// <param name="value"></param>
        /// <param name="defImage"></param>
        /// <returns><see langword="false"/> if the image is empty.</returns>
        public static bool LoadImageFromData(Image? image, object? value, Func<ImageSource>? defImage = null)
        {
            if (image == null) return false;

            BitmapImage? source = null;
            try
            {
                if (value is string str)
                {
                    source = ImageUtils.BitmapImageFromBase64(str);
                }
                else if (value is byte[] data)
                {
                    source = ImageUtils.BitmapImageFromBytes(data);
                }
            }
            catch (Exception e)
            {
                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
            }

            if (source != null)
            {
                image.Source = source;
                return true;
            }
            else
            {
                image.Source = defImage?.Invoke() ?? CreateEmptyImage();
                return false;
            }
        }

        
    }

    public class DFEmbeddedImageControl : DynamicFormFieldControl<DFLayoutEmbeddedImage, DFLayoutEmbeddedImageProperties, Guid, DFLayoutEmbeddedMediaValue>
    {
        private Grid _grid = null!; // Late-initialised
        private Border? _border;
        private Image _image = null!; // Late-initialised
        private bool _isEmpty = true;

        private Button viewButton = null!;
        private Button pasteButton = null!;
        private Button clearButton = null!;
        private Button selectButton = null!;

        private DFLayoutEmbeddedMediaValue _value = null!;

        static DFEmbeddedImageControl()
        {
            IsEnabledProperty.OverrideMetadata(
                typeof(DFEmbeddedImageControl),
                new UIPropertyMetadata(
                    true,
                    ControlIsEnabledChanged));
        }

        private static void ControlIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = (DFEmbeddedImageControl)obj;
            control.IsEnabled = true;

            var enabled = (bool)e.NewValue;

            control.pasteButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
            control.clearButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
            control.selectButton.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
        }

        protected override FrameworkElement Create()
        {
            _value = new DFLayoutEmbeddedMediaValue();
            
            _grid = new Grid();
            _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
            _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

            _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            _grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            _border = new Border()
            {
                BorderBrush = new SolidColorBrush(Colors.Gray),
                BorderThickness = new Thickness(0.75),
                Background = new SolidColorBrush(Colors.White),
                Padding = new Thickness(4),
                Margin = new Thickness(0)
            };
            _border.SetGridPosition(0,0,5,1);
            _grid.Children.Add(_border);
            
            _image = new Image
            {
                Stretch = Stretch.Uniform,
                //StretchDirection = StretchDirection.DownOnly,
                Source = EmbeddedImageUtilities.CreateEmptyImage(),
            };
            _border.Child = _image;
            
            viewButton = new Button
            {
                Content = Content = new Image { Source = Wpf.Resources.zoomin.AsBitmapImage(24, 24), Margin = new Thickness(2) },
                Margin = new Thickness(5, 0, 0, 0),
                Width = 30,
                Height = 30
            };
            viewButton.Click += ViewButton_Click;
            viewButton.SetGridPosition(0,1);
            _grid.Children.Add(viewButton);
            
            pasteButton = new Button
            {
                Content = Content = new Image { Source = Wpf.Resources.paste.AsBitmapImage(24, 24) },
                Margin = new Thickness(5, 5, 0, 0),
                Width = 30,
                Height = 30
            };
            pasteButton.Click += PasteButton_Click;
            pasteButton.SetGridPosition(2, 1);
            _grid.Children.Add(pasteButton);

            clearButton = new Button
            {
                Content = Content = new Image { Source = Wpf.Resources.delete.AsBitmapImage(24, 24) },
                Margin = new Thickness(5, 5, 0, 0),
                Width = 30,
                Height = 30
            };
            clearButton.Click += EmbeddedImageClear_Click;
            clearButton.SetGridPosition(3,1);
            _grid.Children.Add(clearButton);
            
            selectButton = new Button
            {
                Content = Content = new Image { Source = Wpf.Resources.load.AsBitmapImage(24, 24) },
                Margin = new Thickness(5, 5, 0, 0),
                Width = 30,
                Height = 30
            };
            selectButton.Click += EmbeddedImageSelect_Click;
            selectButton.SetGridPosition(4, 1);
            _grid.Children.Add(selectButton);
            
            return _grid;
        }

        private static void ShowImage(byte[] data)
        {
            var src = ImageUtils.LoadImage(data);
            var editor = new SfImageEditor
            {
                ImageSource = src
            };
            var window = new Window() { Content = editor, Height = 800, Width=800 };
            window.ShowDialog();
        }
        
        private void ViewButton_Click(object sender, RoutedEventArgs e)
        {
            if (_value.Data?.Any() == true)
                ShowImage(_value.Data);
            else
            {
                DigitalFormDocumentFactory.LoadDocument(_value.ID, (data) =>
                {
                    if(data.Length == 0)
                    {
                        Dispatcher.BeginInvoke(() =>
                            MessageWindow.ShowMessage("The image should be in the database, but it is not.", "Document not found", image: MessageWindow.WarningImage));
                    }
                    else
                    {
                        _value.Data = data;
                        Dispatcher.BeginInvoke(() => ShowImage(data));
                    }
                }); 
            }
        }

        private void PasteButton_Click(object sender, RoutedEventArgs e)
        {
            if (Clipboard.ContainsImage())
            {
                var paste = Clipboard.GetImage();
                var bitmap = ImageUtils.BitmapSourceToBitmap(paste);
                var img = ImageUtils.ToBitmapImage(bitmap);
                var empty = img == null;
                if (_isEmpty != empty)
                {
                    _isEmpty = empty;

                    if (empty)
                        _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
                    else
                        _image.Source = img;

                    SetSerializedValue(new DFLayoutEmbeddedMediaValue
                    {
                        Data = EmbeddedImageUtilities.SaveImageToBytes(_image, _isEmpty, new JpegBitmapEncoder()) ?? Array.Empty<byte>()
                    });

                    ChangeField();
                }
            }
        }

        public override void SetSerializedValue(DFLayoutEmbeddedMediaValue value)
        {
            _value = value;
            _value.Thumbnail ??= _value.Data != null
                ? ImageUtils.BitmapImageFromBytes(_value.Data)?.Resize(200, 200).ToArray<BmpBitmapEncoder>()
                : null;
            _isEmpty = !EmbeddedImageUtilities.LoadImageFromData(_image, _value.Data?.Any() == true ? _value.Data : _value.Thumbnail);
        }

        public override DFLayoutEmbeddedMediaValue GetSerializedValue()
        {
            if ((_value.Data?.Any() == true) && (_value.ID == Guid.Empty))
                _value.ID = DigitalFormDocumentFactory.SaveDocument(_value.Data);
            return _value;
        }

        public override Guid GetValue()
        {
            return _value.ID;
        }

        public override void SetValue(Guid value)
        {
            SetSerializedValue(new DFLayoutEmbeddedMediaValue() { ID = value });
        }

        private void EmbeddedImageClear_Click(object sender, RoutedEventArgs e)
        {
            if (!_isEmpty)
            {
                _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
                _isEmpty = true;

                SetSerializedValue(new DFLayoutEmbeddedMediaValue());

                ChangeField();
            }
        }

        private void EmbeddedImageSelect_Click(object sender, RoutedEventArgs e)
        {
            if (EmbeddedImageUtilities.SelectImageFile(out var data))
            {
                var img = ImageUtils.BitmapImageFromBytes(data);
                var empty = img == null;
                if (_isEmpty != empty)
                {
                    _isEmpty = empty;

                    if (empty)
                        _image.Source = EmbeddedImageUtilities.CreateEmptyImage();
                    else
                        _image.Source = img;

                    SetSerializedValue(new DFLayoutEmbeddedMediaValue
                    {
                        Data = data
                    });

                    ChangeField();
                }
            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == IsEnabledProperty)
            {
                _grid.RowDefinitions[1].Height = (bool)e.NewValue
                    ? GridLength.Auto
                    : new GridLength(0);
            }
        }
        protected override bool IsEmpty() => _isEmpty;
    }
}
