Editor/Sprite/SpriteEditor/Preview/RenderingWidget.cs
using Editor;
using Sandbox;
using System.Collections.Generic;
using System.Linq;

namespace SpriteTools.SpriteEditor.Preview;

public class RenderingWidget : SpriteRenderingWidget
{
	MainWindow MainWindow;

	public Draggable OriginMarker;
	List<Draggable> Attachments = new();
	public Draggable LastDragged = null;

	Draggable dragging = null;
	Vector3 draggableGrabPos = Vector3.Zero;
	bool holdingControl = false;

	public RenderingWidget ( MainWindow window, Widget parent ) : base( parent )
	{
		MainWindow = window;

		var markerMaterial = Material.Load( "materials/sprite_editor_origin.vmat" );
		OriginMarker = new Draggable( GizmoInstance.World, "models/preview_quad.vmdl", Transform.Zero );
		OriginMarker.SetMaterialOverride( markerMaterial );
		OriginMarker.Tags.Add( "origin" );
		OriginMarker.Position = new Vector3( 0, 0, 1f );
		OriginMarker.Flags.WantsFrameBufferCopy = true;
		OriginMarker.Flags.IsTranslucent = true;
		OriginMarker.Flags.IsOpaque = false;
		OriginMarker.Flags.CastShadows = false;
		OriginMarker.OnPositionChanged = MoveOrigin;
	}

	void MoveOrigin ( Vector2 pos )
	{
		if ( MainWindow.SelectedAnimation is null ) return;

		var origin = ( pos / new Vector2( 100, 100 / AspectRatio ) ) + ( Vector2.One * 0.5f );
		if ( AspectRatio < 1f )
			origin = ( pos / new Vector2( 100 * AspectRatio, 100 ) ) + ( Vector2.One * 0.5f );
		if ( !holdingControl )
		{
			origin = origin.SnapToGrid( 1f / TextureSize.x, true, false );
			origin = origin.SnapToGrid( 1f / TextureSize.y, false, true );
		}

		MainWindow.SelectedAnimation.Origin = origin;
	}

	protected override void OnMousePress ( MouseEvent e )
	{
		base.OnMousePress( e );

		if ( e.LeftMouseButton )
		{
			var cursorLocalPos = Editor.Application.CursorPosition - ScreenRect.Position;
			var tr = GizmoInstance.World.Trace.Ray( Camera.ScreenPixelToRay( cursorLocalPos ), 8000f ).WithTag( "draggable" ).Run();
			Log.Info( tr.EndPosition );
			if ( tr.SceneObject is Draggable draggable )
			{
				dragging = draggable;
				draggableGrabPos = tr.EndPosition.WithZ( 0f );
				LastDragged = draggable;
			}
			else
			{
				LastDragged = null;
			}
			OnDragSelected?.Invoke();
		}
	}

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

		if ( dragging is not null )
		{
			var cursorLocalPos = Editor.Application.CursorPosition - ScreenRect.Position;
			var tr = GizmoInstance.World.Trace.Ray( Camera.ScreenPixelToRay( cursorLocalPos ), 5000f ).WithTag( "draggable" ).Run();
			var pos = tr.EndPosition.WithZ( 0f );
			draggableGrabPos = pos;
			dragging?.OnPositionChanged.Invoke( new Vector2( pos.y, pos.x ) );
		}
	}

	protected override void OnMouseReleased ( MouseEvent e )
	{
		base.OnMouseReleased( e );

		if ( dragging is not null )
		{
			dragging = null;
		}
	}

	protected override void OnKeyPress ( KeyEvent e )
	{
		base.OnKeyPress( e );

		if ( e.Key == KeyCode.Control )
		{
			holdingControl = true;
		}
	}

	protected override void OnKeyRelease ( KeyEvent e )
	{
		base.OnKeyRelease( e );

		if ( e.Key == KeyCode.Control )
		{
			holdingControl = false;
		}
	}

	protected override void PreFrame ()
	{
		base.PreFrame();
		var sizeVec = new Vector2( 100, 100 / AspectRatio );
		if ( AspectRatio < 1f )
			sizeVec = new Vector2( 100 * AspectRatio, 100 );

		float scale = Camera.OrthographicHeight / 1024f;
		if ( MainWindow.SelectedAnimation is not null )
		{
			OriginMarker.RenderingEnabled = true;
			var origin = MainWindow.SelectedAnimation.Origin;
			origin -= Vector2.One * 0.5f;
			origin *= sizeVec;
			OriginMarker.Position = new Vector3( origin.y, origin.x, 1f );
			OriginMarker.Transform = OriginMarker.Transform.WithScale( new Vector3( scale, scale, 1f ) );
		}
		else
		{
			OriginMarker.RenderingEnabled = false;
		}

		scale /= 1.5f;
		foreach ( var attachmentWidget in Attachments )
		{
			attachmentWidget.RenderingEnabled = false;
		}

		foreach ( var attachment in MainWindow.SelectedAnimation?.Attachments ?? new List<SpriteAttachment>() )
		{
			if ( attachment is null ) continue;
			var name = attachment.Name.ToLowerInvariant();
			if ( string.IsNullOrWhiteSpace( name ) ) continue;
			var attach = Attachments.FirstOrDefault( a => a.Tags.Has( name ) );
			var has = Attachments.Any( a => a.Tags.Has( name ) );
			if ( !has )
			{
				var markerMaterial = Material.Load( "materials/sprite_editor_attachment.vmat" ).CreateCopy();
				attach = new Draggable( GizmoInstance.World, "models/preview_quad.vmdl", Transform.Zero );
				attach.SetMaterialOverride( markerMaterial );
				attach.Tags.Add( name );
				attach.Position = new Vector3( 0, 0, 10f );
				attach.Transform = attach.Transform.WithRotation( new Angles( 0, 45, 0 ) );
				attach.Flags.WantsFrameBufferCopy = true;
				attach.Flags.IsTranslucent = true;
				attach.Flags.IsOpaque = false;
				attach.Flags.CastShadows = false;
				attach.OnPositionChanged = ( Vector2 pos ) =>
				{
					if ( MainWindow.SelectedAnimation is null ) return;

					var attachPos = ( pos / sizeVec ) + ( Vector2.One * 0.5f );
					if ( !holdingControl )
					{
						attachPos = attachPos.SnapToGrid( 1f / TextureSize.x, true, false );
						attachPos = attachPos.SnapToGrid( 1f / TextureSize.y, false, true );
					}

					var currentAttachment = MainWindow.SelectedAnimation.Attachments.FirstOrDefault( a => a.Name.ToLowerInvariant() == name );
					if ( currentAttachment is null ) return;

					var index = MainWindow.CurrentFrameIndex;
					for ( int i = currentAttachment.Points.Count; i <= index; i++ )
					{
						currentAttachment.Points.Add( attachPos );
					}
					currentAttachment.Points[index] = attachPos;

					// Log.Info($"{currentAttachment.} - {name} - {index} - {attachPos}");
				};
				Attachments.Add( attach );
			}
			else
			{
				if ( MainWindow.SelectedAnimation is not null )
				{
					if ( MainWindow.CurrentFrameIndex < attachment.Points.Count )
					{
						var attachPos = attachment.Points[MainWindow.CurrentFrameIndex];
						attachPos -= Vector2.One * 0.5f;
						attachPos *= sizeVec;
						attach.Position = new Vector3( attachPos.y, attachPos.x, 10f );
					}
					else
					{
						for ( int i = attachment.Points.Count - 1; i >= 0; i-- )
						{
							if ( attachment.Points.Count > i )
							{
								var attachPos1 = attachment.Points[i];
								attachPos1 -= Vector2.One * 0.5f;
								attachPos1 *= sizeVec;
								attach.Position = new Vector3( attachPos1.y, attachPos1.x, 10f );
								break;
							}
						}
					}
				}
			}
			if ( attachment.Visible )
			{
				attach.RenderingEnabled = true;
			}
			attach.Transform = attach.Transform.WithScale( new Vector3( scale, scale, 1f ) );
			attach.ColorTint = attachment.Color;
		}
	}
}