using System;
using System.Xml.Serialization;
using Newtonsoft.Json;
using SabreTools.Hashing;
using SabreTools.Metadata.DatItems.Formats;
using SabreTools.Metadata.Filter;
namespace SabreTools.Metadata.DatItems
{
///
/// Base class for all items included in a set
///
[JsonObject("datitem"), XmlRoot("datitem")]
[XmlInclude(typeof(Adjuster))]
[XmlInclude(typeof(Analog))]
[XmlInclude(typeof(Archive))]
[XmlInclude(typeof(BiosSet))]
[XmlInclude(typeof(Blank))]
[XmlInclude(typeof(Chip))]
[XmlInclude(typeof(Condition))]
[XmlInclude(typeof(Configuration))]
[XmlInclude(typeof(ConfLocation))]
[XmlInclude(typeof(ConfSetting))]
[XmlInclude(typeof(Control))]
[XmlInclude(typeof(DataArea))]
[XmlInclude(typeof(Device))]
[XmlInclude(typeof(DeviceRef))]
[XmlInclude(typeof(DipLocation))]
[XmlInclude(typeof(DipSwitch))]
[XmlInclude(typeof(DipValue))]
[XmlInclude(typeof(Disk))]
[XmlInclude(typeof(DiskArea))]
[XmlInclude(typeof(Display))]
[XmlInclude(typeof(Driver))]
[XmlInclude(typeof(Extension))]
[XmlInclude(typeof(Feature))]
[XmlInclude(typeof(Info))]
[XmlInclude(typeof(Input))]
[XmlInclude(typeof(Instance))]
[XmlInclude(typeof(Media))]
[XmlInclude(typeof(Part))]
[XmlInclude(typeof(PartFeature))]
[XmlInclude(typeof(Port))]
[XmlInclude(typeof(RamOption))]
[XmlInclude(typeof(Release))]
[XmlInclude(typeof(ReleaseDetails))]
[XmlInclude(typeof(Rom))]
[XmlInclude(typeof(Sample))]
[XmlInclude(typeof(SharedFeat))]
[XmlInclude(typeof(Slot))]
[XmlInclude(typeof(SlotOption))]
[XmlInclude(typeof(SoftwareList))]
[XmlInclude(typeof(Sound))]
[XmlInclude(typeof(SourceDetails))]
public abstract class DatItem : ICloneable, IEquatable
{
#region Properties
///
/// Duplicate type when compared to another item
///
public DupeType DupeType { get; set; } = 0x00;
///
/// Item type for the object
///
public abstract Data.Models.Metadata.ItemType ItemType { get; }
///
/// Get the machine for a DatItem
///
public Machine? Machine { get; set; }
///
/// Flag if item should be removed
///
public bool RemoveFlag { get; set; } = false;
///
/// Source information
///
public Source? Source { get; set; }
#endregion
#region Accessors
///
/// Gets the name to use for a DatItem
///
/// Name if available, null otherwise
public abstract string? GetName();
///
/// Sets the name to use for a DatItem
///
/// Name to set for the item
public abstract void SetName(string? name);
#endregion
#region Cloning Methods
///
/// Clone the DatItem
///
/// Clone of the DatItem
public abstract object Clone();
///
/// Copy all machine information over in one shot
///
/// Existing item to copy information from
public void CopyMachineInformation(DatItem item)
{
// If there is no machine
if (item.Machine is null)
return;
CopyMachineInformation(item.Machine);
}
///
/// Copy all machine information over in one shot
///
/// Existing machine to copy information from
public void CopyMachineInformation(Machine? machine)
{
if (machine is null)
return;
if (machine.Clone() is Machine cloned)
Machine = cloned;
}
#endregion
#region Comparision Methods
///
/// Determine if an item is a duplicate using partial matching logic
///
/// DatItem to use as a baseline
/// True if the items are duplicates, false otherwise
public abstract bool Equals(DatItem? other);
#endregion
#region Manipulation
///
/// Runs a filter and determines if it passes or not
///
/// Filter runner to use for checking
/// True if the item and its machine passes the filter, false otherwise
public abstract bool PassesFilter(FilterRunner filterRunner);
///
/// Runs a filter and determines if it passes or not
///
/// Filter runner to use for checking
/// True if the item passes the filter, false otherwise
public abstract bool PassesFilterDB(FilterRunner filterRunner);
#endregion
#region Sorting and Merging
///
/// Get the dictionary key that should be used for a given item and bucketing type
///
/// ItemKey value representing what key to get
/// Machine associated with the item for renaming
/// Source associated with the item for renaming
/// True if the key should be lowercased (default), false otherwise
/// True if games should only be compared on game and file name, false if system and source are counted
/// String representing the key to be used for the DatItem
public virtual string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
{
// Set the output key as the default blank string
string key = string.Empty;
string sourceKeyPadded = source?.Index.ToString().PadLeft(10, '0') + '-';
string machineName = machine?.Name ?? "Default";
// Now determine what the key should be based on the bucketedBy value
switch (bucketedBy)
{
case ItemKey.CRC16:
key = HashType.CRC16.ZeroString;
break;
case ItemKey.CRC32:
key = HashType.CRC32.ZeroString;
break;
case ItemKey.CRC64:
key = HashType.CRC64.ZeroString;
break;
case ItemKey.Machine:
key = (norename ? string.Empty : sourceKeyPadded) + machineName;
break;
case ItemKey.MD2:
key = HashType.MD2.ZeroString;
break;
case ItemKey.MD4:
key = HashType.MD4.ZeroString;
break;
case ItemKey.MD5:
key = HashType.MD5.ZeroString;
break;
case ItemKey.RIPEMD128:
key = HashType.RIPEMD128.ZeroString;
break;
case ItemKey.RIPEMD160:
key = HashType.RIPEMD160.ZeroString;
break;
case ItemKey.SHA1:
key = HashType.SHA1.ZeroString;
break;
case ItemKey.SHA256:
key = HashType.SHA256.ZeroString;
break;
case ItemKey.SHA384:
key = HashType.SHA384.ZeroString;
break;
case ItemKey.SHA512:
key = HashType.SHA512.ZeroString;
break;
case ItemKey.SpamSum:
key = HashType.SpamSum.ZeroString;
break;
case ItemKey.NULL:
default:
// This should never happen
break;
}
// Double and triple check the key for corner cases
key ??= string.Empty;
if (lower)
key = key.ToLowerInvariant();
return key;
}
#endregion
}
}