using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Xml; using System.Xml.Serialization; using DynamicData; using Intromat.Helpers; using Intromat.PersistentModel.Nodes; using Intromat.ViewModels; using Intromat.ViewModels.Nodes; using NodeNetwork.ViewModels; namespace Intromat.PersistentModel { internal class ProjectSerializer { public static async Task LoadProject(MainViewModel mainVm, string fullPath) { await using var stream = new FileStream(fullPath, FileMode.Open); var projectSerializer = new XmlSerializer(typeof(ProjectModel)); var project = (ProjectModel)projectSerializer.Deserialize(stream)!; var projectVm = new ProjectViewModel(project.Name, Path.GetFullPath(fullPath)); var directory = Path.GetDirectoryName(projectVm.FullPath)!; foreach (var modulePath in project.Modules) { var moduleFullPath = Path.Combine(directory, modulePath); var moduleVm = await LoadModule(mainVm, projectVm, moduleFullPath); projectVm.Modules.Add(moduleVm); } return projectVm; } public static async Task LoadModule(MainViewModel mainVm, ProjectViewModel parent, string fullPath) { var moduleVm = new ModuleViewModel(parent, Path.GetFileName(fullPath)!, fullPath); await LoadFolder(mainVm, moduleVm, moduleVm, moduleVm, fullPath); return moduleVm; } private static async Task LoadFolder(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel parent, FolderViewModel target, string folderPath) { foreach (var subFolderPath in Directory.EnumerateDirectories(folderPath)) { var subFolderVm = new FolderViewModel(moduleVm, parent, Path.GetDirectoryName(folderPath)!); await LoadFolder(mainVm, moduleVm, target, subFolderVm, subFolderPath); target.Folders.Add(subFolderVm); } foreach (var filePath in Directory.EnumerateFiles(folderPath)) { switch (Path.GetExtension(filePath)) { case ".igraph": { target.Files.Add(await LoadDocument(mainVm, moduleVm, target, filePath)); break; } case ".hlsl": { target.Files.Add(await LoadShader(mainVm, moduleVm, target, filePath)); break; } } } } private static async Task LoadDocument(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel folderVm, string filePath) { await using var stream = new FileStream(filePath, FileMode.Open); var networkSerializer = new XmlSerializer(typeof(NetworkModel)); var network = (NetworkModel)networkSerializer.Deserialize(stream)!; var documentVm = new DocumentViewModel(mainVm, moduleVm, folderVm, Path.GetFileNameWithoutExtension(filePath)); LoadNetwork(documentVm.MainNetwork, network, documentVm); return documentVm; } private static async Task LoadShader(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel folderVm, string filePath) { await using var stream = new FileStream(filePath, FileMode.Open); var shaderFileVm = new ShaderFileViewModel(mainVm, moduleVm, folderVm, Path.GetFileNameWithoutExtension(filePath)); using var reader = new StreamReader(stream); shaderFileVm.Source = await reader.ReadToEndAsync(); return shaderFileVm; } public static CodeGenNetworkViewModel LoadNetwork(CodeGenNetworkViewModel networkVm, NetworkModel network, DocumentViewModel documentVm, GroupNodeViewModel? baseGroup = null) { PreLoadNetwork(network, documentVm, baseGroup, networkVm, out var nodeMap); PostLoadNetwork(network, networkVm, nodeMap); return networkVm; } private static void PreLoadNetwork(NetworkModel network, DocumentViewModel documentVm, GroupNodeViewModel? baseGroup, CodeGenNetworkViewModel networkVm, out Dictionary outNodeMap) { var nodeMap = outNodeMap = new Dictionary(); GroupSubnetIONodeViewModel? entrance = null, exit = null; var groups = new List(); networkVm.Nodes.Edit(nodes => { foreach (var node in network.Nodes.Items) { var nodeVm = node.CreateViewModel()!; nodeVm.LoadModel(node); nodes.Add(nodeVm); nodeMap.Add(node.Guid, nodeVm); if (nodeVm is GroupSubnetIONodeViewModel subnetIONodeVm) { Debug.Assert(baseGroup != null, nameof(baseGroup) + " != null"); if (subnetIONodeVm.IsEntranceNode) { entrance = subnetIONodeVm; } else { exit = subnetIONodeVm; } } else if (node is GroupModel group) { groups.Add(group); } } }); if (baseGroup != null && entrance != null && exit != null) { var groupBinding = new CodeNodeGroupIOBinding(baseGroup, entrance, exit); baseGroup.IOBinding = groupBinding; entrance.IOBinding = groupBinding; exit.IOBinding = groupBinding; } foreach (var group in groups) { var groupVm = (GroupNodeViewModel)nodeMap[group.Guid]; var subNetworkVm = new CodeGenNetworkViewModel(documentVm, group.Name); PreLoadNetwork(group.Network, documentVm, groupVm, subNetworkVm, out var subNetworkNodeMap); var ioBinding = groupVm.IOBinding!; groupVm.Inputs.Edit(inputs => { foreach (var input in group.Inputs) { var valueType = Type.GetType(input.EndpointType)!; var inputVm = ioBinding.CreateInput(input.PortType, input.Name, valueType); inputVm.SortIndex = input.SortIndex; inputs.Add(inputVm); } }); groupVm.Outputs.Edit(outputs => { foreach (var output in group.Outputs) { var valueType = Type.GetType(output.EndpointType)!; var outputVm = ioBinding.CreateOutput(output.PortType, output.Name, valueType, output.List); outputVm.SortIndex = output.SortIndex; outputs.Add(outputVm); } }); groupVm.Subnet = subNetworkVm; groupVm.Name = subNetworkVm.Name; PostLoadNetwork(group.Network, subNetworkVm, subNetworkNodeMap); } } private static void PostLoadNetwork(NetworkModel network, CodeGenNetworkViewModel networkVm, Dictionary nodeMap) { networkVm.Connections.Edit(connections => { foreach (var connection in network.Connections) { var inputVm = nodeMap[connection.Input.Node]; var outputVm = nodeMap[connection.Output.Node]; var input = inputVm.Inputs.Items.Single(i => i.Name == connection.Input.Name); var output = outputVm.Outputs.Items.Single(o => o.Name == connection.Output.Name); ((CodeGenPortViewModel)input.Port).IsPortVisible = true; ((CodeGenPortViewModel)output.Port).IsPortVisible = true; var connectionVm = new ConnectionViewModel(networkVm, input, output); connections.Add(connectionVm); } }); foreach (var metaData in network.MetaData) { var nodeVm = nodeMap[metaData.Node]; nodeVm.Position = new Point(metaData.Position.X, metaData.Position.Y); nodeVm.IsCollapsed = metaData.IsCollapsed; } } public static async Task SaveModel(ProjectViewModel projectVm) { var directoryName = Path.GetDirectoryName(projectVm.FullPath)!; Directory.CreateDirectory(directoryName); await using var stream = new FileStream(projectVm.FullPath, FileMode.Create); await using var writer = new XmlTextWriter(stream, Encoding.UTF8) { Formatting = Formatting.Indented }; var project = new ProjectModel { Name = projectVm.Name }; foreach (var moduleVm in projectVm.Modules) { project.Modules.Add(IOHelper.GetRelativePath(directoryName, moduleVm.FullPath)); await SaveModel(moduleVm); } var projectSerializer = new XmlSerializer(typeof(ProjectModel)); projectSerializer.Serialize(writer, project, NodeCollectionModel._nameSpaces); return project; } public static async Task SaveModel(FolderViewModel folderVm) { foreach (var subFolderVm in folderVm.Folders.Items) { await SaveModel(subFolderVm); } foreach (var documentVm in folderVm.Files.Items) { await SaveModel(documentVm); } } private static async Task SaveModel(FileViewModel fileVm) { Directory.CreateDirectory(Path.GetDirectoryName(fileVm.FullPath)!); await using var stream = new FileStream(fileVm.FullPath, FileMode.Create); switch (fileVm) { case DocumentViewModel documentVm: { var network = SaveModel(documentVm.MainNetwork); await using var writer = new XmlTextWriter(stream, Encoding.UTF8) { Formatting = Formatting.Indented }; var moduleSerializer = new XmlSerializer(typeof(NetworkModel)); moduleSerializer.Serialize(writer, network, NodeCollectionModel._nameSpaces); break; } case ShaderFileViewModel shaderVm: { var shader = shaderVm.Source; await using var writer = new StreamWriter(stream); await writer.WriteAsync(shader); break; } default: { throw new NotImplementedException(); } } } public static NetworkModel SaveModel(CodeGenNetworkViewModel networkVm) { Debug.Assert(networkVm.Name != null, $"{nameof(networkVm)}.{nameof(networkVm.Name)} != null"); var network = new NetworkModel(); foreach (var item in networkVm.Nodes.Items) { var nodeVm = (CodeGenNodeViewModel)item; var node = nodeVm.CreateModel(); nodeVm.SaveModel(node); network.Nodes.Items.Add(node); } foreach (var connectionVm in networkVm.Connections.Items) { var connection = new ConnectionModel { Input = new EndpointModel { Name = connectionVm.Input.Name, Node = ((CodeGenNodeViewModel)connectionVm.Input.Parent).Guid }, Output = new EndpointModel { Name = connectionVm.Output.Name, Node = ((CodeGenNodeViewModel)connectionVm.Output.Parent).Guid }, }; network.Connections.Add(connection); } foreach (var nodeVm in networkVm.Nodes.Items) { var metaData = new NodeMetaData { Position = new PositionModel { X = nodeVm.Position.X, Y = nodeVm.Position.Y }, IsCollapsed = nodeVm.IsCollapsed, Node = ((CodeGenNodeViewModel)nodeVm).Guid }; network.MetaData.Add(metaData); } return network; } } }