using System;
using Syncfusion.XForms.PopupLayout;
using Xamarin.Forms;

namespace InABox.Mobile
{
    public interface IPopupManager
    {
        bool Initialise();
        
    }

    public class PopupManagerConfiguration
    {

        public static double? DefaultHeight => 700;
        public static double? DefaultWidth => 350;
        public static double? AutoSize => null;
        
        public static RelativePosition? CenterOnScreen => null;
        
        public double? RequestedHeight { get; set; } = DefaultHeight;
        public double? RequestedWidth { get; set; } = DefaultWidth;
        
        public AutoSizeMode SizeMode
        {
            get => (Equals(RequestedHeight,AutoSize) && Equals(RequestedWidth,AutoSize))
                ? AutoSizeMode.Both
                : Equals(RequestedHeight,AutoSize)
                    ? AutoSizeMode.Height
                    : Equals(RequestedWidth,AutoSize)
                        ? AutoSizeMode.Width
                        : AutoSizeMode.None;
            set
            {
                RequestedHeight = value is AutoSizeMode.Both or AutoSizeMode.Height ? AutoSize : DefaultHeight;
                RequestedWidth = value is AutoSizeMode.Both or AutoSizeMode.Width ? AutoSize : DefaultWidth;
            }
        }
        
        public bool ShowHeader { get; set; } = false;
        public bool ShowFooter { get; set; } = false;
        public int Padding { get; set; } = 10;
        public int CornerRadius { get; set; } = 5;
        public bool Modal { get; set; } = false;

        public RelativePosition? Position { get; set; } = CenterOnScreen;
    }
    
    public static class PopupManager
    {
        
        private static SfPopupLayout _popup = null;
        
        private static ContentPage FindParentPage(Element view)
        {
            if (view is ContentPage page)
                return page;
            return FindParentPage(view.Parent);
        }
        
        public static void DisplayError(Element view, string message)
        {
            MobileLogging.Log("Unable to create Popup Layout!");
            var page = FindParentPage(view);
            Device.BeginInvokeOnMainThread(() =>
            { 
                page.DisplayAlert("Error", message, "OK");
            }); 
        }

        public static bool Initialise()
        {
            var service = DependencyService.Get<IPopupManager>();
            return service?.Initialise() == true;
        }

        public static void ShowPopup(VisualElement parent, Func<View> view, PopupManagerConfiguration config = null)
        {
            
            if (_popup == null && Initialise())
                _popup = new SfPopupLayout();

            if (_popup == null)
            {
                DisplayError(parent, "Unable to create Popup!\nPlease try again or restart the application.");
                return;
            }

            var cfg = config ?? new PopupManagerConfiguration();
            
            _popup.PopupView.AutoSizeMode = cfg.SizeMode;

            if (Equals(cfg.RequestedHeight, PopupManagerConfiguration.AutoSize))
                _popup.PopupView.ClearValue(VisualElement.HeightRequestProperty);
            else
                _popup.PopupView.HeightRequest = cfg.RequestedHeight!.Value;
            
            if (Equals(cfg.RequestedWidth, PopupManagerConfiguration.AutoSize))
                _popup.PopupView.ClearValue(VisualElement.WidthRequestProperty);
            else
                _popup.PopupView.WidthRequest = cfg.RequestedWidth!.Value;
            
            _popup.PopupView.ShowHeader = cfg.ShowHeader;
            _popup.PopupView.ShowFooter = cfg.ShowFooter;
            _popup.PopupView.PopupStyle.CornerRadius = cfg.CornerRadius;
            _popup.StaysOpen = cfg.Modal;
            
            _popup.PopupView.ContentTemplate = new DataTemplate(() =>
            {
                Grid grid = new Grid() { Padding = cfg.Padding };
                grid.Children.Add(view());
                return grid;
            });
            
            if (parent is View pView && cfg.Position != PopupManagerConfiguration.CenterOnScreen)
            {
                var offset = GetOffset(parent, cfg.Position!.Value); 
                _popup.ShowRelativeToView(pView, cfg.Position!.Value, offset.X, offset.Y);
            }
            else
                _popup.Show();
        }
        
        /// <summary>
        /// Calculates the offest of the Menu to position it at the center of the Button
        ///  Let's not presume that all the calculations are correct - its only been tested
        /// against AlignToLeftOf (for top-right-hand side menu options 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        private static Point GetOffset(VisualElement parent, RelativePosition Position)
        {
            return Position switch
            {

                // Displays the popup at the top of the given view.
                RelativePosition.AlignTop => new Point(parent.Width / 2F, parent.Height / 2F),

                // Displays the popup to the left of the given view.</summary>
                RelativePosition.AlignToLeftOf => new Point(parent.Width / 2F, parent.Height / 2F),

                // Displays the popup to the right of the given view.</summary>
                RelativePosition.AlignToRightOf => new Point(0F - (parent.Width / 2F), parent.Height / 2F),

                // Displays the popup at the bottom of the given view.</summary>
                RelativePosition.AlignBottom => new Point(parent.Width / 2F, 0F - (parent.Height / 2F)),

                // Displays the popup at the top left position of the given view.
                RelativePosition.AlignTopLeft => new Point(parent.Width / 2F, parent.Height / 2F),

                // Displays the popup at the top right position of the given view.
                RelativePosition.AlignTopRight => new Point(0F - (parent.Width / 2F), parent.Height / 2F),

                // Displays the popup at the bottom left position of the given view.
                RelativePosition.AlignBottomLeft => new Point(0F - (parent.Width / 2F), parent.Height / 2F),

                // Displays the popup at the bottom right position of the given view.
                RelativePosition.AlignBottomRight => new Point(0F - (parent.Width / 2F), 0F - (parent.Height / 2F)),

                _ => new Point()
            };
        }

        public static void DismissPopup()
        {
            if (_popup != null)
                _popup.Dismiss();
        }
    }
}