using System; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Filter; namespace SabreTools.DatItems { /// /// Represents the information specific to a set/game/machine /// [JsonObject("machine"), XmlRoot("machine")] public class Machine : ICloneable { #region Constants /// /// Trurip/EmuArc Machine developer /// public const string DeveloperKey = "DEVELOPER"; /// /// Trurip/EmuArc Game genre /// public const string GenreKey = "GENRE"; /// /// Trurip/EmuArc Title ID /// public const string TitleIDKey = "TITLEID"; #endregion #region Fields /// /// Internal Machine model /// [JsonIgnore] private Models.Metadata.Machine _machine = []; #endregion #region Constructors public Machine() { } public Machine(Models.Metadata.Machine machine) { // Get all fields to automatically copy without processing var nonItemFields = TypeHelper.GetConstants(typeof(Models.Metadata.Machine)); if (nonItemFields == null) return; // Populate the internal machine from non-filter fields _machine = []; foreach (string fieldName in nonItemFields) { if (machine.ContainsKey(fieldName)) _machine[fieldName] = machine[fieldName]; } } #endregion #region Accessors /// /// Get the value from a field based on the type provided /// /// Type of the value to get from the internal model /// Field to retrieve /// Value from the field, if possible public T? GetFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName)) return default; // Get the value based on the type return _machine.Read(fieldName!); } /// /// Get the value from a field based on the type provided /// /// Field to retrieve /// Value from the field, if possible public bool? GetBoolFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName) || !_machine.ContainsKey(fieldName!)) return default; // Get the value based on the type return _machine.ReadBool(fieldName!); } /// /// Get the value from a field based on the type provided /// /// Field to retrieve /// Value from the field, if possible public double? GetDoubleFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName) || !_machine.ContainsKey(fieldName!)) return default; // Get the value based on the type return _machine.ReadDouble(fieldName!); } /// /// Get the value from a field based on the type provided /// /// Field to retrieve /// Value from the field, if possible public long? GetInt64FieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName) || !_machine.ContainsKey(fieldName!)) return default; // Get the value based on the type return _machine.ReadLong(fieldName!); } /// /// Get the value from a field based on the type provided /// /// Field to retrieve /// Value from the field, if possible public string? GetStringFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName) || !_machine.ContainsKey(fieldName!)) return default; // Get the value based on the type return _machine.ReadString(fieldName!); } /// /// Get the value from a field based on the type provided /// /// Field to retrieve /// Value from the field, if possible public string[]? GetStringArrayFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName) || !_machine.ContainsKey(fieldName!)) return default; // Get the value based on the type return _machine.ReadStringArray(fieldName!); } /// /// Set the value from a field based on the type provided /// /// Type of the value to set in the internal model /// Field to set /// Value to set /// True if the value was set, false otherwise public bool SetFieldValue(string? fieldName, T? value) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName)) return false; // Set the value based on the type _machine[fieldName!] = value; return true; } /// /// Get a clone of the current internal model /// public Models.Metadata.Machine GetInternalClone() => (_machine.Clone() as Models.Metadata.Machine)!; #endregion #region Cloning methods /// /// Create a clone of the current machine /// /// New machine with the same values as the current one public object Clone() { return new Machine() { _machine = this._machine.Clone() as Models.Metadata.Machine ?? [], }; } #endregion #region Manipulation /// /// Runs a filter and determines if it passes or not /// /// Filter runner to use for checking /// True if the Machine passes the filter, false otherwise public bool PassesFilter(FilterRunner filterRunner) => filterRunner.Run(_machine); /// /// Remove a field from the Machine /// /// Field to remove /// True if the removal was successful, false otherwise public bool RemoveField(string? fieldName) => FieldManipulator.RemoveField(_machine, fieldName); /// /// Replace a field from another Machine /// /// Machine to replace field from /// Field to replace /// True if the replacement was successful, false otherwise public bool ReplaceField(Machine? other, string? fieldName) => FieldManipulator.ReplaceField(other?._machine, _machine, fieldName); /// /// Set a field in the Machine from a mapping string /// /// Field to set /// String representing the value to set /// True if the setting was successful, false otherwise /// This only performs minimal validation before setting public bool SetField(string? fieldName, string value) => FieldManipulator.SetField(_machine, fieldName, value); #endregion } }