using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using CustomCollections;
namespace Decal.Adapter.IDQueue;
///
/// A scheduler: callers request actions. Multiple callers can request the same action.
/// -Each caller gets one action, then the other callers get a turn.
/// -If a new caller arrives, it is given priority in the turns list.
/// -Everytime a caller gets a turn, it selects its next available action in round-robin fashion.
/// -If all of a caller's actions are unavailable when it is that caller's turn, its turn is lost and it must wait in line again.
/// -If no caller can take a turn, the null action is returned.
/// Thus if multiple callers request the same action, it will be tried more often
/// since the attempt will occur during the turns of multiple callers.
///
///
///
[CLSCompliant(true)]
[ClassInterface(ClassInterfaceType.None)]
public abstract class FairRoundRobinScheduleQueue
{
public class ActionRemovedEventArgs : EventArgs
{
private ACTIONTYPE mAction;
public ACTIONTYPE Action => mAction;
internal ActionRemovedEventArgs(ACTIONTYPE pAction)
{
mAction = pAction;
}
}
private class ActionInfo : IEquatable
{
public ACTIONTYPE Action;
public DateTime Expires;
public int TryCount;
public ActionInfo(ACTIONTYPE pAction, DateTime pExpires)
{
Action = pAction;
Expires = pExpires;
}
public bool IsExpired()
{
return Expires < DateTime.Now;
}
public bool Equals(ActionInfo other)
{
ref ACTIONTYPE action = ref Action;
object obj = other.Action;
return action.Equals(obj);
}
public override bool Equals(object obj)
{
return Equals(obj as ActionInfo);
}
public override int GetHashCode()
{
return Action.GetHashCode();
}
}
private HashedList Callers = new HashedList();
private Dictionary ActInfos = new Dictionary();
private Dictionary> CallerActions = new Dictionary>();
private Dictionary> ActionCallers = new Dictionary>();
private int MaximumTryCount;
private ACTIONTYPE NullAction;
public int CallerCount => Callers.Count;
public int ActionCount => ActInfos.Count;
public event EventHandler OnActionRemoved;
///
///
///
/// The most times an action can be attempted before it fails.
/// An ACTIONTYPE to return when no action is available.
protected FairRoundRobinScheduleQueue(int pMaximumTryCount, ACTIONTYPE pNullAction)
{
MaximumTryCount = pMaximumTryCount;
NullAction = pNullAction;
}
protected abstract bool IsActionValidNow(ACTIONTYPE action);
protected abstract bool IsActionPermanentlyInvalid(ACTIONTYPE action);
private void RotateQueue(HashedList q)
{
if (q.Count > 0)
{
T item = q[0];
q.RemoveAt(0);
q.Add(item);
}
}
public void ClearAll()
{
Callers.Clear();
ActInfos.Clear();
CallerActions.Clear();
ActionCallers.Clear();
}
public void DeleteAction(ACTIONTYPE action)
{
if (!ActionCallers.ContainsKey(action) || !ActInfos.ContainsKey(action))
{
return;
}
ActionInfo item = ActInfos[action];
ActInfos.Remove(action);
HashedList hashedList = ActionCallers[action];
ActionCallers.Remove(action);
foreach (CALLERTYPE item2 in hashedList)
{
CallerActions[item2].Remove(item);
if (CallerActions[item2].Count == 0)
{
CallerActions.Remove(item2);
Callers.Remove(item2);
}
}
if (this.OnActionRemoved != null)
{
this.OnActionRemoved(this, new ActionRemovedEventArgs(action));
}
}
public void DeleteCaller(CALLERTYPE caller)
{
Callers.Remove(caller);
if (!CallerActions.ContainsKey(caller))
{
return;
}
HashedList hashedList = CallerActions[caller];
CallerActions.Remove(caller);
HashedList hashedList2 = new HashedList();
foreach (ActionInfo item in hashedList)
{
if (ActionCallers.ContainsKey(item.Action))
{
ActionCallers[item.Action].Remove(caller);
if (ActionCallers[item.Action].Count == 0)
{
ActionCallers.Remove(item.Action);
ActInfos.Remove(item.Action);
hashedList2.Add(item);
}
}
}
if (this.OnActionRemoved == null)
{
return;
}
foreach (ActionInfo item2 in hashedList2)
{
this.OnActionRemoved(this, new ActionRemovedEventArgs(item2.Action));
}
}
public ReadOnlyCollection GetActionsForCaller(CALLERTYPE caller)
{
List list = new List();
if (CallerActions.ContainsKey(caller))
{
foreach (ActionInfo item in CallerActions[caller])
{
list.Add(item.Action);
}
}
return list.AsReadOnly();
}
public ReadOnlyCollection GetCallersForAction(ACTIONTYPE action)
{
if (ActionCallers.ContainsKey(action))
{
return ActionCallers[action].AsReadOnly();
}
return new List().AsReadOnly();
}
public void Enqueue(CALLERTYPE caller, ACTIONTYPE action, DateTime expiration)
{
ActionInfo actionInfo;
if (ActInfos.ContainsKey(action))
{
actionInfo = ActInfos[action];
if (actionInfo.Expires < expiration)
{
actionInfo.Expires = expiration;
}
actionInfo.TryCount = 0;
}
else
{
actionInfo = new ActionInfo(action, expiration);
ActInfos[action] = actionInfo;
}
if (!CallerActions.ContainsKey(caller) || !CallerActions[caller].Contains(actionInfo))
{
if (!Callers.Contains(caller))
{
Callers.Insert(0, caller);
}
if (!CallerActions.ContainsKey(caller))
{
CallerActions.Add(caller, new HashedList());
}
CallerActions[caller].Add(actionInfo);
if (!ActionCallers.ContainsKey(action))
{
ActionCallers.Add(action, new HashedList());
}
ActionCallers[action].Add(caller);
}
}
public ACTIONTYPE GetNextAction(ref CALLERTYPE requester)
{
HashedList hashedList = new HashedList();
ACTIONTYPE result = NullAction;
for (int i = 0; i < Callers.Count; i++)
{
CALLERTYPE val = Callers[0];
HashedList hashedList2 = CallerActions[val];
bool flag = false;
for (int j = 0; j < hashedList2.Count; j++)
{
ActionInfo actionInfo = hashedList2[0];
if (hashedList.Contains(actionInfo))
{
RotateQueue(hashedList2);
continue;
}
if (actionInfo.IsExpired())
{
hashedList.Add(actionInfo);
RotateQueue(hashedList2);
continue;
}
if (IsActionPermanentlyInvalid(actionInfo.Action))
{
hashedList.Add(actionInfo);
RotateQueue(hashedList2);
continue;
}
if (!IsActionValidNow(actionInfo.Action))
{
RotateQueue(hashedList2);
continue;
}
flag = true;
result = actionInfo.Action;
requester = val;
RotateQueue(hashedList2);
actionInfo.TryCount++;
if (actionInfo.TryCount > MaximumTryCount)
{
hashedList.Add(actionInfo);
}
break;
}
RotateQueue(Callers);
if (flag)
{
break;
}
}
foreach (ActionInfo item in hashedList)
{
DeleteAction(item.Action);
}
return result;
}
}