using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Questionable.Model.V1; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Questionable.QuestPathGenerator; public static class RoslynShortcuts { public static IEnumerable SyntaxNodeList(params SyntaxNodeOrToken?[] nodes) { nodes = nodes.Where(x => x != null && x.Value.RawKind != 0).ToArray(); if (nodes.Length == 0) return []; List list = new(); for (int i = 0; i < nodes.Length; ++i) { if (i > 0) list.Add(Token(SyntaxKind.CommaToken)); list.Add(nodes[i].GetValueOrDefault()); } return list; } public static ExpressionSyntax LiteralValue(T? value) { try { if (value is string s) return LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(s)); else if (value is bool b) return LiteralExpression(b ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression); else if (value is short i16) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i16)); else if (value is int i32) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i32)); else if (value is byte u8) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(u8)); else if (value is ushort u16) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(u16)); else if (value is uint u32) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(u32)); else if (value is float f) return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(f)); else if (value != null && value.GetType().IsEnum) return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(value.GetType().Name), IdentifierName(value.GetType().GetEnumName(value)!)); else if (value is Vector3 vector) { return ObjectCreationExpression( IdentifierName(nameof(Vector3))) .WithArgumentList( ArgumentList( SeparatedList( new SyntaxNodeOrToken[] { Argument(LiteralValue(vector.X)), Token(SyntaxKind.CommaToken), Argument(LiteralValue(vector.Y)), Token(SyntaxKind.CommaToken), Argument(LiteralValue(vector.Z)) }))); } else if (value is AethernetShortcut aethernetShortcut) { return ObjectCreationExpression( IdentifierName(nameof(AethernetShortcut))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( Assignment(nameof(AethernetShortcut.From), aethernetShortcut.From, null) .AsSyntaxNodeOrToken(), Assignment(nameof(AethernetShortcut.To), aethernetShortcut.To, null) .AsSyntaxNodeOrToken())))); } else if (value is ChatMessage chatMessage) { ChatMessage emptyMessage = new(); return ObjectCreationExpression( IdentifierName(nameof(ChatMessage))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( Assignment(nameof(ChatMessage.ExcelSheet), chatMessage.ExcelSheet, emptyMessage.ExcelSheet) .AsSyntaxNodeOrToken(), Assignment(nameof(ChatMessage.Key), chatMessage.Key, emptyMessage.Key) .AsSyntaxNodeOrToken())))); } else if (value is DialogueChoice dialogueChoice) { DialogueChoice emptyChoice = new(); return ObjectCreationExpression( IdentifierName(nameof(DialogueChoice))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( Assignment(nameof(DialogueChoice.Type), dialogueChoice.Type, null) .AsSyntaxNodeOrToken(), Assignment(nameof(DialogueChoice.ExcelSheet), dialogueChoice.ExcelSheet, emptyChoice.ExcelSheet) .AsSyntaxNodeOrToken(), Assignment(nameof(DialogueChoice.Prompt), dialogueChoice.Prompt, emptyChoice.Prompt) .AsSyntaxNodeOrToken(), Assignment(nameof(DialogueChoice.Yes), dialogueChoice.Yes, emptyChoice.Yes) .AsSyntaxNodeOrToken(), Assignment(nameof(DialogueChoice.Answer), dialogueChoice.Answer, emptyChoice.Answer) .AsSyntaxNodeOrToken(), Assignment(nameof(DialogueChoice.DataId), dialogueChoice.DataId, emptyChoice.DataId) .AsSyntaxNodeOrToken())))); } else if (value is JumpDestination jumpDestination) { return ObjectCreationExpression( IdentifierName(nameof(JumpDestination))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( Assignment(nameof(JumpDestination.Position), jumpDestination.Position, null) .AsSyntaxNodeOrToken(), Assignment(nameof(JumpDestination.StopDistance), jumpDestination.StopDistance, null) .AsSyntaxNodeOrToken(), Assignment(nameof(JumpDestination.DelaySeconds), jumpDestination.DelaySeconds, null) .AsSyntaxNodeOrToken())))); } else if (value is ExcelRef excelRef) { if (excelRef.Type == ExcelRef.EType.Key) { return ObjectCreationExpression( IdentifierName(nameof(ExcelRef))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument(LiteralValue(excelRef.AsKey()))))); } else if (excelRef.Type == ExcelRef.EType.RowId) { return ObjectCreationExpression( IdentifierName(nameof(ExcelRef))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument(LiteralValue(excelRef.AsRowId()))))); } else throw new Exception($"Unsupported ExcelRef type {excelRef.Type}"); } else if (value is ComplexCombatData complexCombatData) { var emptyData = new ComplexCombatData(); return ObjectCreationExpression( IdentifierName(nameof(ComplexCombatData))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( Assignment(nameof(ComplexCombatData.DataId), complexCombatData.DataId, emptyData.DataId) .AsSyntaxNodeOrToken(), Assignment(nameof(ComplexCombatData.MinimumKillCount), complexCombatData.MinimumKillCount, emptyData.MinimumKillCount) .AsSyntaxNodeOrToken(), Assignment(nameof(ComplexCombatData.RewardItemId), complexCombatData.RewardItemId, emptyData.RewardItemId) .AsSyntaxNodeOrToken(), Assignment(nameof(ComplexCombatData.RewardItemCount), complexCombatData.RewardItemCount, emptyData.RewardItemCount) .AsSyntaxNodeOrToken(), AssignmentList(nameof(ComplexCombatData.CompletionQuestVariablesFlags), complexCombatData.CompletionQuestVariablesFlags), Assignment(nameof(ComplexCombatData.IgnoreQuestMarker), complexCombatData.IgnoreQuestMarker, emptyData.IgnoreQuestMarker) .AsSyntaxNodeOrToken())))); } else if (value is QuestWorkValue qwv) { return ObjectCreationExpression( IdentifierName(nameof(QuestWorkValue))) .WithArgumentList( ArgumentList( SeparatedList( new SyntaxNodeOrToken[] { Argument(LiteralValue(qwv.High)), Token(SyntaxKind.CommaToken), Argument(LiteralValue(qwv.Low)) }))); } else if (value is List list) { return CollectionExpression( SeparatedList( SyntaxNodeList(list.Select(x => ExpressionElement( LiteralValue(x)).AsSyntaxNodeOrToken()).ToArray()))); } else if (value is null) return LiteralExpression(SyntaxKind.NullLiteralExpression); } catch (Exception e) { throw new Exception($"Unable to handle literal [{value}]: {e.StackTrace}", e); } throw new Exception($"Unsupported data type {value.GetType()} = {value}"); } public static AssignmentExpressionSyntax? Assignment(string name, T? value, T? defaultValue) { try { if (value == null && defaultValue == null) return null; if (value != null && defaultValue != null && value.Equals(defaultValue)) return null; return AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(name), LiteralValue(value)); } catch (Exception e) { throw new Exception($"Unable to handle assignment [{name}]: {e.Message}", e); } } public static AssignmentExpressionSyntax? AssignmentList(string name, IEnumerable? value) { try { if (value == null) return null; IEnumerable list = value.ToList(); if (!list.Any()) return null; return AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(name), CollectionExpression( SeparatedList( SyntaxNodeList(list.Select(x => ExpressionElement( LiteralValue(x)).AsSyntaxNodeOrToken()).ToArray()) ))); } catch (Exception e) { throw new Exception($"Unable to handle list [{name}]: {e.StackTrace}", e); } } public static SyntaxNodeOrToken? AsSyntaxNodeOrToken(this SyntaxNode? node) { if (node == null) return null; return node; } }