using System;
using System.Collections.Generic;
using System.Linq;
namespace JD.Kernel.Structs
{
///
/// Encapsulate a flag enum and contains helper functions to manipulate it.
///
public struct Flags : IEquatable
where T : IComparable, IFormattable
{
#region Variables
private T m_value;
#endregion
#region Constructor
static Flags()
{
Type type = typeof (T);
if (!type.IsEnum)
{
throw new InvalidCastException($"The flags type cannot be used with the non enum type {typeof(T).Name}");
}
if (!type.IsDefined(typeof(FlagsAttribute), true))
{
throw new InvalidCastException($"The enum type {typeof(T).Name} should be decorated with the flags attribute. Consider using a BitArray");
}
if (Enum.GetUnderlyingType(type) != typeof(int))
{
throw new NotSupportedException("Flags class only support int enums.");
}
}
public Flags(T value)
{
m_value = value;
}
#endregion
#region Properties
///
/// Returns the actual wrapped value.
///
public T Value
{
get { return m_value; }
}
private int IntValue
{
get { return ToInt(m_value); }
}
///
/// Gets/Sets the value of the given flagKey.
///
/// Flag to access.
/// The value of the flag.
public bool this[T flagKey]
{
get { return Contains(flagKey); }
set
{
if (value)
{
Add(flagKey);
}
else
{
Remove(flagKey);
}
}
}
#endregion
#region Functions
private static int ToInt(T value)
{
return (int)(object)value;
}
///
/// Removes the given flag from the enum value.
///
///
/// True if the flag has been removed.
public bool Remove(T flag)
{
int value = IntValue;
int flagToExtract = ToInt(flag);
int newValue = value & ~flagToExtract;
m_value = (T)(object)newValue;
return value != newValue;
}
///
/// Adds the given flag to the internal value.
///
///
/// True if the flag has been added. (false if it was already present)
public bool Add(T flag)
{
int value = IntValue;
int flagToAdd = ToInt(flag);
int newValue = value | flagToAdd;
m_value = (T)(object)newValue;
return value != newValue;
}
///
/// True if the flag contains that given value.
///
/// Value to test for
/// True if the flag contains that given value.
public bool Contains(T value)
{
int flag = ToInt(value);
return (IntValue & flag) == flag;
}
///
/// True if the flag contains EVERY values.
///
/// First value to test for
/// Additional values to test for.
/// True if the flag contains EVERY values.
public bool Contains(T value, params T[] otherValues)
{
if (!Contains(value))
{
return false;
}
foreach (T t in otherValues)
{
if (!Contains(t))
{
return false;
}
}
return true;
}
///
/// True if any of the given values are contained in the flag.
///
/// First value to test for
/// Additional values to test for.
/// True if any of the given values are contained in the flag.
public bool ContainsAny(T value, params T[] otherValues)
{
if (Contains(value))
{
return true;
}
foreach (T other in otherValues)
{
if (Contains(other))
{
return true;
}
}
return false;
}
///
/// True if the flag has any overlap with the given .
///
/// Value to test for.
/// True if the flag has any overlap with the given .
public bool Intersects(T value)
{
int flag = ToInt(value);
return (IntValue & flag) != 0;
}
#endregion
#region Comparison
///
/// True if the enum equals our value.
///
///
///
public bool Equals(T other)
{
return Equals(m_value, other);
}
///
/// True if the enum equals our value.
///
public override bool Equals(object obj)
{
if (obj is Flags)
{
return Equals(((Flags)obj).m_value, m_value);
}
return Equals(m_value, obj);
}
///
/// Hashcode.
///
///
public override int GetHashCode()
{
return m_value.GetHashCode();
}
///
/// True if the flag value is any of the given values.
///
///
///
/// True if the flag value is any of the given values.
public bool EqualsAny(T first, params T[] other)
{
if (Equals(first))
{
return true;
}
return other.Any(Equals);
}
#endregion
#region Operators
///
/// == Operator.
///
public static bool operator ==(Flags a, Flags b)
{
return Equals((T)a, (T)b);
}
///
/// != Operator.
///
public static bool operator !=(Flags a, Flags b)
{
return !Equals((T)a, (T)b);
}
///
/// Implicit cast Operator.
///
public static implicit operator T(Flags value)
{
return value.m_value;
}
///
/// Implicit cast Operator.
///
public static implicit operator Flags(T value)
{
return new Flags(value);
}
public static Flags operator |(Flags a, Flags b)
{
a.Add(b);
return a;
}
#endregion
#region ToString
///
/// Returns the display of the inner Value.
///
public override string ToString()
{
return $"Flags '{m_value}'";
}
#endregion
#region Utilities
///
/// All the defined flags defined in the enum.
///
public static IEnumerable Values
{
get { return Enum.GetValues(typeof (T)).Cast(); }
}
#endregion
}
}