Editor/RigControlEditor/RigAnimExportDialog.cs
using Editor;
using Marionette;
using Sandbox;
using System;
using System.IO;

namespace Marionette.Tools;

/// <summary>
/// The export button's dialog: a clip name, a loop toggle, and one action.
///
/// A DIALOG RATHER THAN A SILENT FILE WRITE. Exporting a clip into a model has two names that
/// matter (the sequence AnimGraph will ask for, and whether it loops) and a destination that
/// should be visible before anything hits disk. A menu item that just wrote files would look
/// like it did nothing.
/// </summary>
internal sealed class RigAnimExportDialog : Window
{
	private readonly RigAnimDocument _doc;
	private readonly Asset _asset;
	private LineEdit _name;
	private Checkbox _loop;

	public RigAnimExportDialog( Widget owner, RigAnimDocument doc, Asset asset, bool looping )
	{
		_doc = doc;
		_asset = asset;

		Parent = owner;

		WindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.CloseButton
			| WindowFlags.WindowSystemMenuHint | WindowFlags.WindowTitle;

		WindowTitle = "Export Animation";
		Size = new Vector2( 540, 420 );
		SetWindowIcon( "file_download" );

		Build( looping );
	}

	private void Build( bool looping )
	{
		var canvas = new Widget( this ) { Layout = Layout.Column() };
		canvas.Layout.Margin = 14;
		canvas.Layout.Spacing = 10;

		canvas.Layout.Add( new Editor.Label(
			"Interaction clips (open a fridge, pull a lever) usually skip this compile. Save the "
			+ ".riganim, add RigAnimPlayerComponent next to the character's SkinnedModelRenderer, "
			+ "assign the clip, uncheck Loop and Play On Start, and call Play() from your use code. "
			+ "Tween the fridge with player.NormalizedTime so the door and the hands share a clock." )
		{ WordWrap = true } );

		canvas.Layout.Add( new Editor.Label(
			"Use Export when you need a named sequence on a compiled model (AnimGraph, "
			+ "renderer.Sequence.Name). That is a different path, not a requirement." )
		{ WordWrap = true, Color = Theme.TextControl } );

		var nameRow = canvas.Layout.AddRow();
		nameRow.Spacing = 8;
		nameRow.Add( new Editor.Label( "Sequence Name" ) { FixedWidth = 110 } );

		var defaultName = Path.GetFileNameWithoutExtension( _asset?.Name ?? "clip" );
		_name = nameRow.Add( new LineEdit( defaultName ), 1 );
		_name.ToolTip = "What the game asks for: renderer.Sequence.Name = this. Does not have to match the file name.";

		var loopRow = canvas.Layout.AddRow();
		loopRow.Spacing = 8;
		_loop = loopRow.Add( new Checkbox( "Looping" ) { Value = looping } );
		_loop.ToolTip = "Idle and walk loop. Fridge-open, fire, reload usually do not.";

		var dest = DestinationHint();
		canvas.Layout.Add( new Editor.Label( dest ) { WordWrap = true, Color = Theme.TextControl } );

		canvas.Layout.AddStretchCell();

		var buttons = canvas.Layout.AddRow();
		buttons.Spacing = 8;
		buttons.AddStretchCell();
		buttons.Add( new Button( "Cancel" ) { Clicked = Close } );

		var go = buttons.Add( new Button( "Export", "file_download" ) { Clicked = Run } );
		go.ToolTip = "Write the .dmx and .vmdl next to this clip, then compile";

		Canvas = canvas;
	}

	private string DestinationHint()
	{
		var model = _doc?.SourceModel?.Name ?? "(no model)";
		var folder = string.IsNullOrEmpty( _asset?.Path )
			? "Assets"
			: Path.GetDirectoryName( _asset.Path.Replace( '/', Path.DirectorySeparatorChar ) )
				?.Replace( '\\', '/' ) ?? "Assets";

		return $"Writes into {folder}/  ·  Base Model: {model}";
	}

	private void Run()
	{
		var result = RigAnimExport.Export( _doc, _asset, _name?.Text, _loop?.Value ?? true );

		if ( !result.Ok )
		{
			RigStatusBar.Show( result.Error );
			new PopupWindow( "Export Failed", result.Error, "OK",
				new System.Collections.Generic.Dictionary<string, System.Action>
				{
					{ "OK", () => { } }
				} ).Show();
			return;
		}

		var body = result.Compiled
			? $"Compiled {result.VmdlAssetPath}\n\n"
				+ $"Sequence playback:\n"
				+ $"  renderer.UseAnimGraph = false;\n"
				+ $"  renderer.Sequence.Name = \"{result.SequenceName}\";\n\n"
				+ $"Interaction clips (fridge, lever, pickup) are usually easier without this file:\n"
				+ $"  add RigAnimPlayerComponent, assign the .riganim, Loop off, Play On Start off,\n"
				+ $"  call Play() on use, tween the prop with player.NormalizedTime.\n\n"
				+ $"{result.Frames} frames, {result.MatchedBones}/{result.SkeletonBones} bones posed."
			: $"Wrote {result.VmdlAssetPath} but compile failed — check the compiler output.\n\n"
				+ $"The animation file is at {result.DmxAssetPath}. You can add it in ModelDoc "
				+ $"as an AnimFile if you want to finish this by hand.";

		RigStatusBar.Show( result.Compiled
			? $"Exported {result.VmdlAssetPath} — Sequence \"{result.SequenceName}\""
			: $"Wrote {result.VmdlAssetPath} but compile failed" );

		if ( !string.IsNullOrEmpty( result.Note ) )
			body += "\n\n" + result.Note;

		new PopupWindow( "Export Complete", body, "OK",
			new System.Collections.Generic.Dictionary<string, System.Action>
			{
				{ "OK", () => { } }
			} ).Show();

		Close();
	}
}