using System;
using System.Collections.Generic;
using System.Threading;
using Svelto.DataStructures;
namespace Svelto.ECS
{
///
/// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always
/// point to the same group ID.
/// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter,
/// but for this to work, each permutation must be identified by the same ID (generated by the unique combination)
///
static class GroupCompoundInitializer
{
internal static readonly ThreadLocal skipStaticCompoundConstructorsWith4Tags = new ThreadLocal();
internal static readonly ThreadLocal skipStaticCompoundConstructorsWith3Tags = new ThreadLocal();
internal static readonly ThreadLocal skipStaticCompoundConstructorsWith2Tags = new ThreadLocal();
}
public abstract class GroupCompound where G1 : GroupTag
where G2 : GroupTag
where G3 : GroupTag
where G4 : GroupTag
{
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
static int isInitializing;
static GroupCompound()
{
//avoid race conditions if compounds are using on multiple thread
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0
&& GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
{
_Groups = new FasterList(1);
var group = new ExclusiveGroup();
_Groups.Add(group);
var name =
$"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint) group}";
#if DEBUG
GroupNamesMap.idToName[(uint) group] = name;
#endif
GroupHashMap.RegisterGroup(group, name);
_GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _));
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true;
//all the permutations must share the same group and group hashset. Warm them up, avoid call the
//constructors again, set the desired value
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
//all the permutations are warmed up now
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group); // and must share the same array
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group);
GroupCompound.Add(group);
//This is done here to be sure that the group is added once per group tag
//(if done inside the previous group compound it would be added multiple times)
GroupTag.Add(group);
GroupTag.Add(group);
GroupTag.Add(group);
GroupTag.Add(group);
}
}
internal static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
public static bool Includes(ExclusiveGroupStruct @group)
{
return _GroupsHashSet.Contains(@group);
}
}
public abstract class GroupCompound
where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag
{
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
static int isInitializing;
internal static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
public static bool Includes(ExclusiveGroupStruct @group)
{
return _GroupsHashSet.Contains(@group);
}
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
{
_Groups = new FasterList(1);
var group = new ExclusiveGroup();
_Groups.Add(group);
var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint) group}";
#if DEBUG
GroupNamesMap.idToName[(uint) group] = name;
#endif
GroupHashMap.RegisterGroup(group, name);
_GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _));
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;
//all the combinations must share the same group and group hashset
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
GroupCompound._Groups = _Groups;
//all the constructor have been called now
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = false;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound._GroupsHashSet = _GroupsHashSet;
GroupCompound.Add(group); // and must share the same array
GroupCompound.Add(group);
GroupCompound.Add(group);
//This is done here to be sure that the group is added once per group tag
//(if done inside the previous group compound it would be added multiple times)
GroupTag.Add(group);
GroupTag.Add(group);
GroupTag.Add(group);
}
}
}
public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag
{
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
static int isInitializing;
internal static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
public static bool Includes(ExclusiveGroupStruct @group)
{
return _GroupsHashSet.Contains(@group);
}
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
{
var group = new ExclusiveGroup();
_Groups = new FasterList(1);
_Groups.Add(group);
var groupName = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint) group}";
#if DEBUG
GroupNamesMap.idToName[(uint) group] = groupName;
#endif
GroupHashMap.RegisterGroup(group, groupName);
_GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _));
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
GroupCompound._Groups = _Groups;
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;
GroupCompound._GroupsHashSet = _GroupsHashSet;
//every abstract group preemptively adds this group, it may or may not be empty in future
GroupTag.Add(group);
GroupTag.Add(group);
}
}
}
///
///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
///can use the same adjective together with other ones. However since I need to be able to iterate over all the
///groups with the same adjective, a group tag needs to hold all the groups sharing it.
///
///
public abstract class GroupTag where T : GroupTag
{
static readonly FasterList _Groups = new FasterList(1);
static readonly HashSet _GroupsHashSet;
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
static int isInitializing;
static GroupTag()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
{
var group = new ExclusiveGroup();
_Groups.Add(group);
var typeInfo = typeof(T);
var name = $"Compound: {typeInfo.Name} ID {(uint) @group}";
#if DEBUG
var typeInfoBaseType = typeInfo.BaseType;
if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo)
throw new ECSException("Invalid Group Tag declared");
GroupNamesMap.idToName[(uint) group] = name;
#endif
GroupHashMap.RegisterGroup(group, name);
_GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _));
}
}
//Each time a new combination of group tags is found a new group is added.
internal static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
public static bool Includes(ExclusiveGroupStruct @group)
{
return _GroupsHashSet.Contains(@group);
}
}
}