Editor/GraphicsItems/EditableAltCurve.KeyTangent.cs
using Editor;
using System;

namespace AltCurves.GraphicsItems;
public partial class EditableAltCurve
{
	/// <summary>
	/// The movable tangent handle, each visible keyframe contains an in/out handle.
	/// The rendering of the tangent is handled by the KeyVisible.
	/// </summary>
	public class KeyTangent : GraphicsItem
	{
		public const float TANGENT_RADIUS = 50.0f;

		/// <summary>
		/// Specific tangent value, rise/run of the entering/exiting tangent. Positive values slope up, negative slopes down.
		/// </summary>
		public float Tangent {
			get => _tangent;
			set {
				_tangent = value;
				UpdatePosition();
			}
		}
		private float _tangent;

		/// <summary>
		/// Is this an arriving tangent value (rather than a departing one)
		/// </summary>
		private readonly bool _isArrive;

		public CurveWidgetTransform Transform
		{
			get => _transform;
			set
			{
				_transform = value;
				UpdatePosition();
			}
		}
		private CurveWidgetTransform _transform;

		/// <summary>
		/// Hidden state, disables interaction
		/// </summary>
		public bool Hidden { 
			get => _hidden;
			set
			{
				_hidden = value;

				Movable = !value;
				HoverEvents = !value;
				Cursor = value ? CursorShape.None : CursorShape.Finger;
			}
		}
		private bool _hidden = false;

		/// <summary>
		/// Drag operation starting, trigger undo snapshot
		/// </summary>
		public Action OnDragStart;

		/// <summary>
		/// Tangent has been dragged and we have a new slope value based on the handle position
		/// </summary>
		public Action<float> OnUpdated;

		public KeyTangent( GraphicsItem parent, CurveWidgetTransform transform, float initialValue, bool isArrive ) : base( parent )
		{
			HandlePosition = 0.5f;
			Clip = false;
			Selectable = false;
			Size = 10.0f;

			Hidden = false;
			_transform = transform;
			_isArrive = isArrive;
			Tangent = initialValue;
		}

		//protected override void OnPaint()
		//{
		//	base.OnPaint();
		//
		//	Paint.SetBrush( Hovered ? Color.Red : ( _isArrive ? Theme.Blue : Theme.Pink ));
		//	Paint.DrawRect( LocalRect );
		//}

		protected override void OnHoverEnter( GraphicsHoverEvent e )
		{
			base.OnHoverEnter( e );

			// This isn't pretty, but without it there's a weird issue where mousing over the very edge of this
			// tangent correctly gives a cursor finger, but the parent visible keyframe doesn't paint the tangent handle...
			Parent.Update();
		}

		protected override void OnHoverLeave( GraphicsHoverEvent e )
		{
			base.OnHoverLeave( e );
			Parent.Update();
		}

		protected override void OnMousePressed( GraphicsMouseEvent e )
		{
			base.OnMousePressed( e );

			if (e.LeftMouseButton)
				OnDragStart?.Invoke();
		}

		protected override void OnMouseReleased( GraphicsMouseEvent e )
		{
			UpdatePosition(); // Snap drag widget position to correct tangent offset position
		}

		protected override void OnMouseMove( GraphicsMouseEvent e )
		{
			base.OnMouseMove( e );

			// Prevent a tangent being dragged past the opposite side of the keyframe
			var clampedPositionX = _isArrive ? Math.Min( Position.x, -2f ) : Math.Max( 2f, Position.x );
			_tangent = (-Position.y / clampedPositionX) / _transform.WidgetCurveAspectRatio;

			OnUpdated?.Invoke( Tangent );
		}

		/// <summary>
		/// Snap current Position (local to parent) to the correct tangent slope offset position
		/// </summary>
		private void UpdatePosition()
		{
			float widgetSpaceTangent = Tangent * _transform.WidgetCurveAspectRatio;
			var widgetSpaceSlope = new Vector2( 1.0f, -widgetSpaceTangent ).Normal;
			if ( _isArrive ) widgetSpaceSlope = -widgetSpaceSlope;
			Position = widgetSpaceSlope * TANGENT_RADIUS;
		}
	}
}