using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Vanara.Extensions
{
///
/// Structure to use in place of a enumerated type with the set. Allows for indexer access to flags and
/// simplifies boolean logic.
///
/// An enumerated type.
///
/// Use this structure by replacing an enumerated type field for simpler access. See old and new way examples below:
/// var fileInfo = new FileInfo(@"C:\MyFile.txt");
///FileAttributes fileAttr = fileInfo.Attributes;
///if ((fileAttr & FileAttributes.Hidden) != FileAttributes.Hidden)
///{
/// Console.WriteLine("The file is hidden. Trying to unhide now.");
/// fileInfo.Attributes = (fileAttr & ~FileAttributes.Hidden);
///}
/// var fileInfo = new FileInfo(@"C:\MyFile.txt");
///EnumFlagIndexer<FileAttributes> fileAttr = fileInfo.Attributes;
///if (fileAttr[FileAttributes.Hidden])
///{
/// Console.WriteLine("The file is hidden. Trying to unhide now.");
/// fileAttr[FileAttributes.Hidden] = false;
/// fileInfo.Attributes = fileAttr;
///}
///
public struct EnumFlagIndexer : IEquatable, IEquatable>, IEnumerable where TEnum : struct, System.Enum
{
private TEnum flags;
/// Initializes a new instance of the struct.
/// The initial value. Defaults to default(E).
public EnumFlagIndexer(TEnum initialValue)
{
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException($"Type '{typeof(TEnum).FullName}' is not an enum");
}
if (!Attribute.IsDefined(typeof(TEnum), typeof(FlagsAttribute)))
{
throw new ArgumentException($"Type '{typeof(TEnum).FullName}' doesn't have the 'Flags' attribute");
}
flags = initialValue;
}
/// Gets or sets the specified flag.
/// A boolean value representing the presence of the specified enumerated flag.
/// A value in the enumerated type to check.
/// true if the flag is set; false otherwise.
public bool this[TEnum flag]
{
get => (Convert.ToInt64(flags) & Convert.ToInt64(flag)) != 0;
set
{
long flagsValue = Convert.ToInt64(flags);
long flagValue = Convert.ToInt64(flag);
if (value)
{
flagsValue |= flagValue;
}
else
{
flagsValue &= ~flagValue;
}
flags = (TEnum)Enum.ToObject(typeof(TEnum), flagsValue);
}
}
/// Implements the operator !=.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static bool operator !=(EnumFlagIndexer a, TEnum b) => !a.Equals(b);
/// Implements the operator &.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static TEnum operator &(EnumFlagIndexer a, TEnum b) => (TEnum)Enum.ToObject(typeof(TEnum), Convert.ToInt64(a.flags) & Convert.ToInt64(b));
/// Implements the operator |.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static TEnum operator |(EnumFlagIndexer a, TEnum b) => (TEnum)Enum.ToObject(typeof(TEnum), Convert.ToInt64(a.flags) | Convert.ToInt64(b));
/// Implements the operator ==.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static bool operator ==(EnumFlagIndexer a, TEnum b) => a.Equals(b);
/// Implicitly converts an instance of to the value of enumerated type E.
/// The f.
/// The result of the operator.
public static implicit operator TEnum(EnumFlagIndexer f) => f.flags;
/// Implicitly converts a value of E to an instance of .
/// The e.
/// The result of the operator.
public static implicit operator EnumFlagIndexer(TEnum e) => new EnumFlagIndexer(e);
/// Clears and sets to default(E).
public void Clear() => flags = default;
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(TEnum other) => Convert.ToInt64(flags) == Convert.ToInt64(other);
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(EnumFlagIndexer other) => Convert.ToInt64(flags) == Convert.ToInt64(other.flags);
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj) => obj is TEnum e ? Equals(e) : (obj is EnumFlagIndexer i ? Equals(i) : Equals(obj, flags));
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode() => flags.GetHashCode();
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// Returns an enumerator that iterates through the collection.
/// A that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
long t = 0;
foreach (TEnum e in Enum.GetValues(typeof(TEnum)))
{
if (this[e]) { t |= Convert.ToInt64(e); yield return e; }
}
long rem = Convert.ToInt64(flags) ^ t;
if (rem != 0)
{
yield return (TEnum)Enum.ToObject(typeof(TEnum), rem);
}
}
/// Sets the flags to the intersection of the current flags and the specified flags.
/// The flags with which to intersect.
public void Intersect(IEnumerable enumValues) => flags = Enumerable.Intersect(this, enumValues).CombineFlags();
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => flags.ToString();
/// Unions the specified flags.
/// The flags.
public void Union(TEnum enumVal) => this[enumVal] = true;
/// Unions the specified flags.
/// The flags.
public void Union(IEnumerable enumValues)
{
foreach (TEnum e in enumValues)
{
this[e] = true;
}
}
}
}