using System;
using System.Threading.Tasks;
using System.Windows;
using HandyControl.Controls;

namespace HandyControl.Tools.Extension
{
    public interface IDialogResultable<T>
    {
        T Result { get; set; }

        Action CloseAction { get; set; }
    }

    public static class DialogExtension
    {
        public static Task<TResult> GetResultAsync<TResult>(this Dialog dialog)
        {
            var tcs = new TaskCompletionSource<TResult>();

            try
            {
                if (dialog.IsClosed)
                {
                    SetResult();
                }
                else
                {
                    dialog.Unloaded += OnUnloaded;
                    dialog.GetViewModel<IDialogResultable<TResult>>().CloseAction = dialog.Close;
                }
            }
            catch (Exception e)
            {
                tcs.TrySetException(e);
            }

            return tcs.Task;

            // ReSharper disable once ImplicitlyCapturedClosure
            void OnUnloaded(object sender, RoutedEventArgs args)
            {
                dialog.Unloaded -= OnUnloaded;
                SetResult();
            }

            void SetResult()
            {
                try
                {
                    tcs.TrySetResult(dialog.GetViewModel<IDialogResultable<TResult>>().Result);
                }
                catch (Exception e)
                {
                    tcs.TrySetException(e);
                }
            }
        }

        public static Dialog Initialize<TViewModel>(this Dialog dialog, Action<TViewModel> configure)
        {
            configure?.Invoke(dialog.GetViewModel<TViewModel>());

            return dialog;
        }

        public static TViewModel GetViewModel<TViewModel>(this Dialog dialog)
        {
            if (!(dialog.Content is FrameworkElement frameworkElement))
                throw new InvalidOperationException("The dialog is not a derived class of the FrameworkElement. ");

            if (!(frameworkElement.DataContext is TViewModel viewModel))
                throw new InvalidOperationException($"The view model of the dialog is not the {typeof(TViewModel)} type or its derived class. ");

            return viewModel;
        }
    }
}
