//
// CompoundOperation.cs
//
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics;
using Remora.Results;
namespace NosSmooth.Extensions.Combat.Operations;
///
/// An operation made from multiple operations
/// that should execute right after each other.
///
public class CompoundOperation : ICombatOperation
{
private readonly ICombatOperation[] _operations;
private readonly OperationQueueType _queueType;
private Task? _compoundOperation;
///
/// Initializes a new instance of the class.
///
/// The operations to execute.
/// The queue type.
public CompoundOperation
(OperationQueueType queueType = OperationQueueType.TotalControl, params ICombatOperation[] operations)
{
if (operations.Length == 0)
{
throw new ArgumentNullException(nameof(operations), "The compound operation needs at least one operation.");
}
_operations = operations;
_queueType = queueType;
}
///
public void Dispose()
{
foreach (var operation in _operations)
{
operation.Dispose();
}
}
///
public OperationQueueType QueueType { get; }
///
public Task BeginExecution(ICombatState combatState, CancellationToken ct = default)
{
if (_compoundOperation is not null)
{
return Task.FromResult(Result.FromSuccess());
}
_compoundOperation = Task.Run
(
() => UseAsync(combatState, ct),
ct
);
return Task.FromResult(Result.FromSuccess());
}
///
public async Task WaitForFinishedAsync(ICombatState combatState, CancellationToken ct = default)
{
if (IsFinished())
{
return Result.FromSuccess();
}
await BeginExecution(combatState, ct);
if (_compoundOperation is null)
{
throw new UnreachableException();
}
return await _compoundOperation;
}
///
public bool IsExecuting()
=> _compoundOperation is not null && !IsFinished();
///
public bool IsFinished()
=> _compoundOperation?.IsCompleted ?? false;
///
public Result CanBeUsed(ICombatState combatState)
=> _operations[0].CanBeUsed(combatState);
private async Task UseAsync(ICombatState combatState, CancellationToken ct)
{
foreach (var operation in _operations)
{
CanBeUsedResponse canBeUsed = CanBeUsedResponse.MustWait;
while (canBeUsed != CanBeUsedResponse.CanBeUsed)
{
var canBeUsedResult = operation.CanBeUsed(combatState);
if (!canBeUsedResult.IsDefined(out canBeUsed))
{
return Result.FromError(canBeUsedResult);
}
if (canBeUsed == CanBeUsedResponse.WontBeUsable)
{
return new GenericError("Won't be usable.");
}
await Task.Delay(10, ct);
}
var result = await operation.BeginExecution(combatState, ct);
if (!result.IsSuccess)
{
return result;
}
result = await operation.WaitForFinishedAsync(combatState, ct);
if (!result.IsSuccess)
{
return result;
}
}
return Result.FromSuccess();
}
}