using Connected.Annotations;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

public partial class SwipeArea : UIComponent
{
   internal double? _xDown, _yDown;
   private double? _swipeDelta;
   internal ElementReference _componentRef;
   private static readonly string[] _preventDefaultEventNames = { "touchstart", "touchend", "touchcancel" };
   internal int[] _listenerIds;

   [Parameter]
   [Category(CategoryTypes.SwipeArea.Behavior)]
   public RenderFragment ChildContent { get; set; }

   [Parameter]
   [Category(CategoryTypes.SwipeArea.Behavior)]
   public Action<SwipeDirection> OnSwipe { get; set; }

   /// <summary>
   /// Swipe threshold in pixels. If SwipeDelta is below Sensitivity then OnSwipe is not called.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.SwipeArea.Behavior)]
   public int Sensitivity { get; set; } = 100;

   /// <summary>
   /// Prevents default behavior of the browser when swiping.
   /// Usable espacially when swiping up/down - this will prevent the whole page from scrolling up/down.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.SwipeArea.Behavior)]
   public bool PreventDefault { get; set; }

   private bool _preventDefaultChanged;

   public override async Task SetParametersAsync(ParameterView parameters)
   {
      var preventDefault = parameters.GetValueOrDefault<bool>(nameof(PreventDefault));
      if (preventDefault != PreventDefault)
      {
         _preventDefaultChanged = true;
      }

      await base.SetParametersAsync(parameters);
   }

   private async Task SetPreventDefaultInternal(bool value)
   {
      if (value)
      {
         _listenerIds = await _componentRef.AddDefaultPreventingHandlers(_preventDefaultEventNames);
      }
      else
      {
         if (_listenerIds != null)
         {
            await _componentRef.RemoveDefaultPreventingHandlers(_preventDefaultEventNames, _listenerIds);
            _listenerIds = null;
         }
      }
   }

   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
      if (_preventDefaultChanged)
      {
         _preventDefaultChanged = false;
         await SetPreventDefaultInternal(PreventDefault);
      }
   }

   private void OnTouchStart(TouchEventArgs arg)
   {
      _xDown = arg.Touches[0].ClientX;
      _yDown = arg.Touches[0].ClientY;
   }

   internal void OnTouchEnd(TouchEventArgs arg)
   {
      if (_xDown == null || _yDown == null)
         return;

      var xDiff = _xDown.Value - arg.ChangedTouches[0].ClientX;
      var yDiff = _yDown.Value - arg.ChangedTouches[0].ClientY;

      if (Math.Abs(xDiff) < Sensitivity && Math.Abs(yDiff) < Sensitivity)
      {
         _xDown = _yDown = null;
         return;
      }

      if (Math.Abs(xDiff) > Math.Abs(yDiff))
      {
         if (xDiff > 0)
         {
            InvokeAsync(() => OnSwipe(SwipeDirection.RightToLeft));
         }
         else
         {
            InvokeAsync(() => OnSwipe(SwipeDirection.LeftToRight));
         }
         _swipeDelta = xDiff;
      }
      else
      {
         if (yDiff > 0)
         {
            InvokeAsync(() => OnSwipe(SwipeDirection.BottomToTop));
         }
         else
         {
            InvokeAsync(() => OnSwipe(SwipeDirection.TopToBottom));
         }
         _swipeDelta = yDiff;
      }
      _xDown = _yDown = null;
   }

   /// <summary>
   /// The last successful swipe difference in pixels since the last OnSwipe invocation
   /// </summary>
   public double? GetSwipeDelta() => _swipeDelta;

   internal void OnTouchCancel(TouchEventArgs arg)
   {
      _xDown = _yDown = null;
   }
}
