using System.Collections.Generic; using System.Linq; namespace SessionZero.Data; [SzfObject("character_template")] public class CharacterTemplate : SzfObject { public string GameSystem { get; set; } = string.Empty; public List RequiredDatasets { get; set; } = new(); public List Sections { get; set; } = new(); public override void PopulateFromMetadata(Dictionary metadata) { base.PopulateFromMetadata(metadata); if (metadata.TryGetValue("GameSystem", out var gameSystemField)) GameSystem = gameSystemField.Value?.ToString() ?? string.Empty; } public override void ParseSections(List sections) { // Parse [Required Datasets] section var requiredDatasetsSection = sections.FirstOrDefault(s => s.Name.Equals("Required Datasets", StringComparison.OrdinalIgnoreCase)); if (requiredDatasetsSection != null) { foreach (var field in requiredDatasetsSection.Fields) { var reference = DatasetReference.Parse(field.Value?.ToString() ?? string.Empty); if (reference != null) { RequiredDatasets.Add(new RequiredDatasetReference { Alias = field.Name, Reference = reference }); } } } // Parse [Section: ...] and [Section....] sections foreach (var szfSection in sections) { if (szfSection.Name.StartsWith("Section:", StringComparison.OrdinalIgnoreCase)) { var templateSection = new TemplateSection { Name = szfSection.Name.Replace("Section:", "").Trim() }; foreach (var field in szfSection.Fields) { templateSection.Fields.Add(ConvertToTemplateField(field)); } Sections.Add(templateSection); } else if (szfSection.Name.StartsWith("Section.", StringComparison.OrdinalIgnoreCase)) { // Handle subsections by finding their parent var parts = szfSection.Name.Split('.'); if (parts.Length >= 2) { var parentSectionName = parts[0] + ":" + parts[1].Trim(); // Reconstruct parent name var parentSection = Sections.FirstOrDefault(s => s.Name.Equals(parentSectionName.Replace("Section:", "").Trim(), StringComparison.OrdinalIgnoreCase)); if (parentSection != null) { var subSection = new TemplateSection { Name = string.Join(".", parts.Skip(2)).Trim() // Name for the subsection }; foreach (var field in szfSection.Fields) { subSection.Fields.Add(ConvertToTemplateField(field)); } parentSection.Subsections.Add(subSection); } } } } } private TemplateField ConvertToTemplateField(SzfField szfField) { return new TemplateField { Name = szfField.Name, Type = ParseDatasetFieldType(szfField.Type), Value = szfField.Value }; } private DatasetFieldType ParseDatasetFieldType(string typeString) { return typeString.ToLower() switch { "text" => DatasetFieldType.Text, "text-field" => DatasetFieldType.TextField, "number" => DatasetFieldType.Number, "bool" => DatasetFieldType.Boolean, "calculated" => DatasetFieldType.Calculated, "system" => DatasetFieldType.System, "dataset-reference" => DatasetFieldType.DatasetReference, "dataset-type" => DatasetFieldType.DatasetType, "dataset-reference-multiple" => DatasetFieldType.DatasetReferenceMultiple, "dataset-type-multiple" => DatasetFieldType.DatasetTypeMultiple, "group" => DatasetFieldType.Group, _ => DatasetFieldType.Text // Default to text if unknown }; } public override void GenerateMetadata(SzfFieldWriter writer) { // Call base to ensure common metadata is generated first if base had any specific generation logic base.GenerateMetadata(writer); writer.WriteField("GameSystem", "text", GameSystem); } public override void GenerateContent(SzfFieldWriter writer) { // Generate [Required Datasets] section if (RequiredDatasets.Any() || writer.Options.IncludeEmptySections) { writer.AppendSectionHeader("Required Datasets"); foreach (var reqDataset in RequiredDatasets) { writer.WriteField(reqDataset.Alias, "dataset-reference", reqDataset.Reference?.ToString()); } writer.AppendLine(); } // Generate Template Sections foreach (var section in Sections) { GenerateTemplateSection(writer, section, "Section"); writer.AppendLine(); } } private void GenerateTemplateSection(SzfFieldWriter writer, TemplateSection section, string parentPath) { var currentPath = $"{parentPath}: {section.Name}"; writer.AppendSectionHeader(currentPath); foreach (var field in section.Fields) { writer.WriteField(field.Name, field.Type, field.Value); } foreach (var subSection in section.Subsections) { GenerateTemplateSubSection(writer, subSection, $"Section.{section.Name}"); } } private void GenerateTemplateSubSection(SzfFieldWriter writer, TemplateSection subSection, string parentPath) { var currentPath = $"{parentPath}.{subSection.Name}"; writer.AppendSectionHeader(currentPath); foreach (var field in subSection.Fields) { writer.WriteField(field.Name, field.Type, field.Value); } foreach (var nestedSubSection in subSection.Subsections) { // Handle deeper nesting if needed, recursively calling this method GenerateTemplateSubSection(writer, nestedSubSection, currentPath); } } public override SzfValidationResult Validate() { var result = base.Validate(); // Run base validation first if (string.IsNullOrWhiteSpace(GameSystem)) result.AddError("GameSystem is required for CharacterTemplate"); return result; } } public class RequiredDatasetReference { public string Alias { get; set; } = string.Empty; public DatasetReference? Reference { get; set; } } public class TemplateSection { public string Name { get; set; } = string.Empty; public List Fields { get; set; } = new(); public List Subsections { get; set; } = new(); // For nested sections } public class TemplateField { public string Name { get; set; } = string.Empty; public DatasetFieldType Type { get; set; } public object? Value { get; set; } }