using System.Collections.Generic;
using SabreTools.Core.Filter;
using SabreTools.Core.Tools;
using SabreTools.DatFiles;
using SabreTools.DatItems;
using SabreTools.DatItems.Formats;
using SabreTools.IO.Logging;
namespace SabreTools.DatTools
{
///
/// Represents the removal operations that need to be performed on a set of items, usually a DAT
///
public class Remover
{
#region Fields
///
/// List of header fields to exclude from writing
///
public readonly List HeaderFieldNames = [];
///
/// List of machine fields to exclude from writing
///
public readonly List MachineFieldNames = [];
///
/// List of fields to exclude from writing
///
public readonly Dictionary> ItemFieldNames = [];
#endregion
#region Logging
///
/// Logging object
///
protected Logger _logger;
#endregion
#region Constructors
///
/// Constructor
///
public Remover()
{
_logger = new Logger(this);
}
#endregion
#region Population
///
/// Populate the exclusion objects using a field name
///
/// Field name
public void PopulateExclusions(string field)
=> PopulateExclusionsFromList([field]);
///
/// Populate the exclusion objects using a set of field names
///
/// List of field names
public void PopulateExclusionsFromList(List? fields)
{
// If the list is null or empty, just return
if (fields == null || fields.Count == 0)
return;
var watch = new InternalStopwatch("Populating removals from list");
foreach (string field in fields)
{
bool removerSet = SetRemover(field);
if (!removerSet)
_logger.Warning($"The value {field} did not match any known field names. Please check the wiki for more details on supported field names.");
}
watch.Stop();
}
///
/// Set remover from a value
///
/// Key for the remover to be set
private bool SetRemover(string field)
{
// If the key is null or empty, return false
if (string.IsNullOrEmpty(field))
return false;
// Get the parser pair out of it, if possible
try
{
var key = new FilterKey(field);
switch (key.ItemName)
{
case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldNames.Add(key.FieldName);
return true;
case Models.Metadata.MetadataFile.MachineKey:
MachineFieldNames.Add(key.FieldName);
return true;
default:
if (!ItemFieldNames.ContainsKey(key.ItemName))
ItemFieldNames[key.ItemName] = [];
ItemFieldNames[key.ItemName].Add(key.FieldName);
return true;
}
}
catch
{
return false;
}
}
#endregion
#region Running
///
/// Remove fields from a DatFile
///
/// Current DatFile object to run operations on
public void ApplyRemovals(DatFile datFile)
{
InternalStopwatch watch = new("Applying removals to DAT");
RemoveHeaderFields(datFile.Header);
RemoveItemFields(datFile.Items);
RemoveItemFieldsDB(datFile.ItemsDB);
watch.Stop();
}
///
/// Remove header fields with given values
///
public void RemoveHeaderFields(DatHeader? datHeader)
{
// If we have an invalid input, return
if (datHeader == null || HeaderFieldNames.Count == 0)
return;
foreach (var fieldName in HeaderFieldNames)
{
bool removed = datHeader.RemoveField(fieldName);
_logger.Verbose($"Header field {fieldName} {(removed ? "removed" : "could not be removed")}");
}
}
///
/// Apply removals to the item dictionary
///
public void RemoveItemFields(ItemDictionary? itemDictionary)
{
// If we have an invalid input, return
if (itemDictionary == null || (MachineFieldNames.Count == 0 && ItemFieldNames.Count == 0))
return;
foreach (var key in itemDictionary.SortedKeys)
{
List? items = itemDictionary.GetItemsForBucket(key);
if (items == null)
continue;
for (int j = 0; j < items.Count; j++)
{
RemoveFields(items[j]);
}
}
}
///
/// Apply removals to the item dictionary
///
public void RemoveItemFieldsDB(ItemDictionaryDB? itemDictionary)
{
// If we have an invalid input, return
if (itemDictionary == null || (MachineFieldNames.Count == 0 && ItemFieldNames.Count == 0))
return;
// Handle machine removals
foreach (var kvp in itemDictionary.GetMachines())
{
RemoveFields(kvp.Value);
}
// Handle item removals
foreach (var key in itemDictionary.SortedKeys)
{
var items = itemDictionary.GetItemsForBucket(key);
if (items == null)
continue;
foreach (var item in items.Values)
{
RemoveFields(item);
}
}
}
///
/// Remove machine fields with given values
///
private void RemoveFields(Machine? machine)
{
// If we have an invalid input, return
if (machine == null || MachineFieldNames.Count == 0)
return;
foreach (var fieldName in MachineFieldNames)
{
machine.RemoveField(fieldName);
}
}
///
/// Remove fields with given values
///
/// DatItem to remove fields from
private void RemoveFields(DatItem? datItem)
{
if (datItem == null)
return;
#region Common
// Handle Machine fields
var machine = datItem.GetFieldValue(DatItem.MachineKey);
if (MachineFieldNames.Count > 0 && machine != null)
RemoveFields(machine);
// If there are no field names, return
if (ItemFieldNames == null || ItemFieldNames.Count == 0)
return;
// If there are no field names for this type or generic, return
string? itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue().AsStringValue();
if (itemType == null || (!ItemFieldNames.ContainsKey(itemType) && !ItemFieldNames.ContainsKey("item")))
return;
// Get the combined list of fields to remove
var fields = new HashSet();
if (ItemFieldNames.ContainsKey(itemType))
fields.UnionWith(ItemFieldNames[itemType]);
if (ItemFieldNames.ContainsKey("item"))
fields.UnionWith(ItemFieldNames["item"]);
// If the field specifically contains Name, set it separately
if (fields.Contains(Models.Metadata.Rom.NameKey))
datItem.SetName(null);
#endregion
#region Item-Specific
// Handle unnested removals first
foreach (var datItemField in fields)
{
datItem.RemoveField(datItemField);
}
// Handle nested removals
switch (datItem)
{
case Adjuster adjuster: RemoveNestedFields(adjuster); break;
case Configuration configuration: RemoveNestedFields(configuration); break;
case ConfSetting confSetting: RemoveNestedFields(confSetting); break;
case Device device: RemoveNestedFields(device); break;
case DipSwitch dipSwitch: RemoveNestedFields(dipSwitch); break;
case DipValue dipValue: RemoveNestedFields(dipValue); break;
case Disk disk: RemoveNestedFields(disk); break;
case Input input: RemoveNestedFields(input); break;
case Part part: RemoveNestedFields(part); break;
case Port port: RemoveNestedFields(port); break;
case Rom rom: RemoveNestedFields(rom); break;
case Slot slot: RemoveNestedFields(slot); break;
}
#endregion
}
///
/// Remove fields with given values
///
/// Adjuster to remove fields from
private void RemoveNestedFields(Adjuster adjuster)
{
var conditions = adjuster.GetFieldValue(Models.Metadata.Adjuster.ConditionKey) ?? [];
foreach (Condition subCondition in conditions)
{
RemoveFields(subCondition);
}
}
///
/// Remove fields with given values
///
/// Configuration to remove fields from
private void RemoveNestedFields(Configuration configuration)
{
var conditions = configuration.GetFieldValue(Models.Metadata.Configuration.ConditionKey) ?? [];
foreach (Condition subCondition in conditions)
{
RemoveFields(subCondition);
}
var locations = configuration.GetFieldValue(Models.Metadata.Configuration.ConfLocationKey) ?? [];
foreach (ConfLocation subLocation in locations)
{
RemoveFields(subLocation);
}
var settings = configuration.GetFieldValue(Models.Metadata.Configuration.ConfSettingKey) ?? [];
foreach (ConfSetting subSetting in settings)
{
RemoveFields(subSetting);
}
}
///
/// Remove fields with given values
///
/// ConfSetting to remove fields from
private void RemoveNestedFields(ConfSetting confsetting)
{
var conditions = confsetting.GetFieldValue(Models.Metadata.ConfSetting.ConditionKey) ?? [];
foreach (Condition subCondition in conditions)
{
RemoveFields(subCondition);
}
}
///
/// Remove fields with given values
///
/// Device to remove fields from
private void RemoveNestedFields(Device device)
{
var extensions = device.GetFieldValue(Models.Metadata.Device.ExtensionKey) ?? [];
foreach (Extension subExtension in extensions)
{
RemoveFields(subExtension);
}
var instances = device.GetFieldValue(Models.Metadata.Device.InstanceKey) ?? [];
foreach (Instance subInstance in instances)
{
RemoveFields(subInstance);
}
}
///
/// Remove fields with given values
///
/// DipSwitch to remove fields from
private void RemoveNestedFields(DipSwitch dipSwitch)
{
var conditions = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.ConditionKey) ?? [];
foreach (Condition subCondition in conditions)
{
RemoveFields(subCondition);
}
var locations = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipLocationKey) ?? [];
foreach (DipLocation subLocation in locations)
{
RemoveFields(subLocation);
}
var dipValues = dipSwitch.GetFieldValue(Models.Metadata.DipSwitch.DipValueKey) ?? [];
foreach (DipValue subValue in dipValues)
{
RemoveFields(subValue);
}
var part = dipSwitch.GetFieldValue(DipSwitch.PartKey);
if (part != null)
RemoveFields(part);
}
///
/// Remove fields with given values
///
/// DipValue to remove fields from
private void RemoveNestedFields(DipValue dipValue)
{
var conditions = dipValue.GetFieldValue(Models.Metadata.DipValue.ConditionKey) ?? [];
foreach (Condition subCondition in conditions)
{
RemoveFields(subCondition);
}
}
///
/// Remove fields with given values
///
/// Disk to remove fields from
private void RemoveNestedFields(Disk disk)
{
var diskArea = disk.GetFieldValue(Disk.DiskAreaKey);
if (diskArea != null)
RemoveFields(diskArea);
var part = disk.GetFieldValue(Disk.PartKey);
if (part != null)
RemoveFields(part);
}
///
/// Remove fields with given values
///
/// Input to remove fields from
private void RemoveNestedFields(Input input)
{
var controls = input.GetFieldValue(Models.Metadata.Input.ControlKey) ?? [];
foreach (Control subControl in controls)
{
RemoveFields(subControl);
}
}
///
/// Remove fields with given values
///
/// Part to remove fields from
private void RemoveNestedFields(Part part)
{
var features = part.GetFieldValue(Models.Metadata.Part.FeatureKey) ?? [];
foreach (PartFeature subPartFeature in features)
{
RemoveFields(subPartFeature);
}
}
///
/// Remove fields with given values
///
/// Port to remove fields from
private void RemoveNestedFields(Port port)
{
var analogs = port.GetFieldValue(Models.Metadata.Port.AnalogKey) ?? [];
foreach (Analog subAnalog in analogs)
{
RemoveFields(subAnalog);
}
}
///
/// Remove fields with given values
///
/// Rom to remove fields from
private void RemoveNestedFields(Rom rom)
{
var dataArea = rom.GetFieldValue(Rom.DataAreaKey);
if (dataArea != null)
RemoveFields(dataArea);
var part = rom.GetFieldValue(Rom.PartKey);
if (part != null)
RemoveFields(part);
}
///
/// Remove fields with given values
///
/// Slot to remove fields from
private void RemoveNestedFields(Slot slot)
{
var slotOptions = slot.GetFieldValue(Models.Metadata.Slot.SlotOptionKey) ?? [];
foreach (SlotOption subSlotOption in slotOptions)
{
RemoveFields(subSlotOption);
}
}
#endregion
}
}