using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace Aitex.Core.UI.Dialog
{
    /// <summary>
    /// WPFMessageBox.xaml 的交互逻辑
    /// </summary>
    public partial class WPFMessageBox : INotifyPropertyChanged
    {
        private bool _animationRan;

        public WPFMessageBox(Window owner, string message, string details, MessageBoxButton button, MessageBoxImage icon,
                          MessageBoxResult defaultResult, MessageBoxOptions options)
        {
            _animationRan = false;

            InitializeComponent();

            //Owner = owner ?? Application.Current.MainWindow;

            CreateButtons(button, defaultResult);

            CreateImage(icon);

            MessageText.Text = message;

            DetailsExpander.Visibility = string.IsNullOrEmpty(details) ? Visibility.Collapsed : Visibility.Visible;

            DetailsText.Text = details;

            ApplyOptions(options);
        }

        public MessageBoxResult MessageBoxResult { get; set; }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Create Buttons

        /// <summary>
        /// Create the message box's button according to the user's demand
        /// </summary>
        /// <param name="button">The user's buttons selection</param>
        /// <param name="defaultResult">The default button</param>
        private void CreateButtons(MessageBoxButton button, MessageBoxResult defaultResult)
        {
            switch (button)
            {
                case MessageBoxButton.OK:
                    ButtonsPanel.Children.Add(CreateOkButton(defaultResult));
                    break;
                case MessageBoxButton.OKCancel:
                    ButtonsPanel.Children.Add(CreateOkButton(defaultResult));
                    ButtonsPanel.Children.Add(CreateCancelButton(defaultResult));
                    break;
                case MessageBoxButton.YesNoCancel:
                    ButtonsPanel.Children.Add(CreateYesButton(defaultResult));
                    ButtonsPanel.Children.Add(CreateNoButton(defaultResult));
                    ButtonsPanel.Children.Add(CreateCancelButton(defaultResult));
                    break;
                case MessageBoxButton.YesNo:
                    ButtonsPanel.Children.Add(CreateYesButton(defaultResult));
                    ButtonsPanel.Children.Add(CreateNoButton(defaultResult));
                    break;
                default:
                    throw new ArgumentOutOfRangeException("button");
            }
        }

        /// <summary>
        /// Create the ok button on demand
        /// </summary>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private Button CreateOkButton(MessageBoxResult defaultResult)
        {
            var okButton = new Button
            {
                Name = "okButton",
                //Content = ((string)Application.Current.Resources["Confirm"]),
                Content = "确认",
                IsDefault = defaultResult == MessageBoxResult.OK,
                Tag = MessageBoxResult.OK,
            };

            okButton.Click += ButtonClick;

            return okButton;
        }

        /// <summary>
        /// Create the cancel button on demand
        /// </summary>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private Button CreateCancelButton(MessageBoxResult defaultResult)
        {
            var cancelButton = new Button
            {
                Name = "cancelButton",
                //Content = ((string)Application.Current.Resources["Cancel"]),
                Content = "取消",
                IsDefault = defaultResult == MessageBoxResult.Cancel,
                IsCancel = true,
                Tag = MessageBoxResult.Cancel,
            };

            cancelButton.Click += ButtonClick;

            return cancelButton;
        }

        /// <summary>
        /// Create the yes button on demand
        /// </summary>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private Button CreateYesButton(MessageBoxResult defaultResult)
        {
            var yesButton = new Button
            {
                Name = "yesButton",
                //Content = ((string)Application.Current.Resources["Yes"]),
                Content = "Yes",
                IsDefault = defaultResult == MessageBoxResult.Yes,
                Tag = MessageBoxResult.Yes,
            };

            yesButton.Click += ButtonClick;

            return yesButton;
        }

        /// <summary>
        /// Create the no button on demand
        /// </summary>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private Button CreateNoButton(MessageBoxResult defaultResult)
        {
            var noButton = new Button
            {
                Name = "noButton",
                //Content = ((string)Application.Current.Resources["No"]),
                Content = "No",
                IsDefault = defaultResult == MessageBoxResult.No,
                Tag = MessageBoxResult.No,
            };

            noButton.Click += ButtonClick;

            return noButton;
        }

        /// <summary>
        /// The event the buttons trigger. 
        /// Each button hold it's result in the tag, so here it just sets them and close the message box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonClick(object sender, RoutedEventArgs e)
        {
            MessageBoxResult = (MessageBoxResult)(sender as Button).Tag;

            Close();
        }

        #endregion

        private void ApplyOptions(MessageBoxOptions options)
        {
            if ((options & MessageBoxOptions.RightAlign) == MessageBoxOptions.RightAlign)
            {
                MessageText.TextAlignment = TextAlignment.Right;
                DetailsText.TextAlignment = TextAlignment.Right;
            }
            if ((options & MessageBoxOptions.RtlReading) == MessageBoxOptions.RtlReading)
            {
                FlowDirection = FlowDirection.RightToLeft;
            }
        }

        /// <summary>
        /// Create the image from the system's icons
        /// </summary>
        /// <param name="icon"></param>
        private void CreateImage(MessageBoxImage icon)
        {
            switch (icon)
            {
                case MessageBoxImage.None:
                    ImagePlaceholder.Visibility = Visibility.Collapsed;
                    break;
                case MessageBoxImage.Information:
                    ImagePlaceholder.Source = SystemIcons.Information.ToImageSource();
                    break;
                case MessageBoxImage.Question:
                    ImagePlaceholder.Source = SystemIcons.Question.ToImageSource();
                    break;
                case MessageBoxImage.Warning:
                    ImagePlaceholder.Source = SystemIcons.Warning.ToImageSource();
                    break;
                case MessageBoxImage.Error:
                    ImagePlaceholder.Source = SystemIcons.Error.ToImageSource();
                    break;
                default:
                    throw new ArgumentOutOfRangeException("icon");
            }
        }

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler temp = PropertyChanged;
            if (temp != null)
            {
                temp(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Enable dragging
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
                DragMove();
        }

        /// <summary>
        /// Show the startup animation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // This is set here to height after the width has been set 
            // so the details expander won't stretch the message box when it's opened
            SizeToContent = SizeToContent.Height;

            //var animation = TryFindResource("LoadAnimation") as Storyboard;

            //animation.Begin(this);
        }

        /// <summary>
        /// Show the closing animation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MessageBoxWindow_Closing(object sender, CancelEventArgs e)
        {
            if (!_animationRan)
            {
                // The animation won't run if the window is allowed to close, 
                // so here the animation starts, and the window's closing is canceled
                e.Cancel = true;

                var animation = TryFindResource("UnloadAnimation") as Storyboard;

                animation.Completed += AnimationCompleted;

                animation.Begin(this);
            }
        }

        /// <summary>
        /// Signals the closing animation ran, and close the window (for real this time)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AnimationCompleted(object sender, EventArgs e)
        {
            _animationRan = true;

            Close();
        }

        #region Show Information

        /// <summary>
        /// Display an information message
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowInformation(string message, string details = "", bool showCancel = false,
                                                       MessageBoxOptions options = MessageBoxOptions.None)
        {
            return ShowInformation(null, message, details, showCancel, options);
        }

        /// <summary>
        /// Display an information message
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowInformation(Window owner, string message, string details = "",
                                                       bool showCancel = false,
                                                       MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(owner, message, details, showCancel ? MessageBoxButton.OKCancel : MessageBoxButton.OK,
                        MessageBoxImage.Information, MessageBoxResult.OK, options);
        }

        #endregion

        #region Show Question

        /// <summary>
        /// Display a question
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowQuestion(string message, string details = "",
                                                    bool showCancel = false,
                                                    MessageBoxOptions options = MessageBoxOptions.None)
        {
            return ShowQuestion(null, message, details, showCancel, options);
        }

        /// <summary>
        /// Display a question
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowQuestion(Window owner, string message, string details = "",
                                                    bool showCancel = false,
                                                    MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(owner, message, details, showCancel ? MessageBoxButton.YesNoCancel : MessageBoxButton.YesNo,
                        MessageBoxImage.Question, MessageBoxResult.Yes, options);
        }

        #endregion

        #region Show Warning

        /// <summary>
        /// Display a warning
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowWarning(string message, string details = "",
                                                   bool showCancel = false,
                                                   MessageBoxOptions options = MessageBoxOptions.None)
        {
            return ShowWarning(null, message, details, showCancel, options);
        }

        /// <summary>
        /// Display a warning
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowWarning(Window owner, string message, string details = "",
                                                   bool showCancel = false,
                                                   MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(owner, message, details, showCancel ? MessageBoxButton.OKCancel : MessageBoxButton.OK,
                        MessageBoxImage.Warning, MessageBoxResult.OK, options);
        }

        #endregion

        #region Show Error

        /// <summary>
        /// Display an Error
        /// </summary>
        /// <param name="exception">Display the exception's details</param>
        /// <param name="message">The message text</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowError(Exception exception, string message = "",
                                                 MessageBoxOptions options = MessageBoxOptions.None)
        {
            return ShowError(null, exception, message, options);
        }

        /// <summary>
        /// Display an Error
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowError(string message, string details = "",
                                                 bool showCancel = false,
                                                 MessageBoxOptions options = MessageBoxOptions.None)
        {
            return ShowError(null, message, details, showCancel, options);
        }

        /// <summary>
        /// Display an Error
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="exception">Display the exception's details</param>
        /// <param name="message">The message text</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowError(Window owner, Exception exception, string message = "",
                                                 MessageBoxOptions options = MessageBoxOptions.None)
        {
            string details = string.Empty;

#if DEBUG
            details = exception.ToString();
#endif

            return Show(owner, String.IsNullOrEmpty(message) ? exception.Message : message, details, MessageBoxButton.OK,
                        MessageBoxImage.Error, MessageBoxResult.OK, options);
        }

        /// <summary>
        /// Display an Error
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="showCancel">Display the cancel</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult ShowError(Window owner, string message, string details = "",
                                                 bool showCancel = false,
                                                 MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(owner, message, details, showCancel ? MessageBoxButton.OKCancel : MessageBoxButton.OK,
                        MessageBoxImage.Error, MessageBoxResult.OK, options);
        }

        #endregion

        #region Show

        /// <summary>
        /// Show the message box with the specified parameters
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="button">The buttons to be displayed</param>
        /// <param name="icon">The message's severity</param>
        /// <param name="defaultResult">The default button</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult Show(string message, string details = "",
                                            MessageBoxButton button = MessageBoxButton.OK,
                                            MessageBoxImage icon = MessageBoxImage.None,
                                            MessageBoxResult defaultResult = MessageBoxResult.None,
                                            MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(null, message, details, button, icon, defaultResult, options);
        }

        /// <summary>
        /// Show the message box with the specified parameters
        /// </summary>
        /// <param name="message">The message text</param>
        /// <param name="button">The buttons to be displayed</param>
        /// <param name="icon">The message's severity</param>
        /// <param name="defaultResult">The default button</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult Show(string message,
                                            MessageBoxButton button = MessageBoxButton.OK,
                                            MessageBoxImage icon = MessageBoxImage.None,
                                            MessageBoxResult defaultResult = MessageBoxResult.None,
                                            MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(message, string.Empty, button, icon, defaultResult, options);
        }

        /// <summary>
        /// Show the message box with the specified parameters
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="button">The buttons to be displayed</param>
        /// <param name="icon">The message's severity</param>
        /// <param name="defaultResult">The default button</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult Show(Window owner, string message,
                                            MessageBoxButton button = MessageBoxButton.OK,
                                            MessageBoxImage icon = MessageBoxImage.None,
                                            MessageBoxResult defaultResult = MessageBoxResult.None,
                                            MessageBoxOptions options = MessageBoxOptions.None)
        {
            return Show(owner, message, string.Empty, button, icon, defaultResult, options);
        }

        /// <summary>
        /// Show the message box with the specified parameters
        /// </summary>
        /// <param name="owner">The message box's parent window</param>
        /// <param name="message">The message text</param>
        /// <param name="details">The details part text</param>
        /// <param name="button">The buttons to be displayed</param>
        /// <param name="icon">The message's severity</param>
        /// <param name="defaultResult">The default button</param>
        /// <param name="options">Misc options</param>
        /// <returns>The user's selected button</returns>
        public static MessageBoxResult Show(Window owner, string message, string details = "",
                                            MessageBoxButton button = MessageBoxButton.OK,
                                            MessageBoxImage icon = MessageBoxImage.None,
                                            MessageBoxResult defaultResult = MessageBoxResult.None,
                                            MessageBoxOptions options = MessageBoxOptions.None)
        {
            var result = Application.Current.Dispatcher.Invoke(new Func<MessageBoxResult>(() =>
            {
                var messageBox = new WPFMessageBox(owner, message, details, button, icon, defaultResult, options);

                messageBox.ShowDialog();

                return messageBox.MessageBoxResult;
            }));

            return (MessageBoxResult)result;
        }

        #endregion
    }

    public static class IconHelper
    {
        [DllImport("gdi32.dll", SetLastError = true)]
        private static extern bool DeleteObject(IntPtr hObject);

        public static ImageSource ToImageSource(this Icon icon)
        {
            Bitmap bitmap = icon.ToBitmap();
            IntPtr hBitmap = bitmap.GetHbitmap();

            ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
              hBitmap,
              IntPtr.Zero,
              Int32Rect.Empty,
              BitmapSizeOptions.FromEmptyOptions());

            if (!DeleteObject(hBitmap))
            {
                throw new Win32Exception();
            }

            return wpfBitmap.GetAsFrozen() as ImageSource;
        }
    }
}