//
//  CombatState.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.CodeAnalysis;
using NosSmooth.Core.Client;
using NosSmooth.Extensions.Combat.Operations;

namespace NosSmooth.Extensions.Combat;

/// <inheritdoc />
internal class CombatState : ICombatState
{
    private readonly Dictionary<OperationQueueType, LinkedList<ICombatOperation>> _operations;
    private readonly Dictionary<OperationQueueType, ICombatOperation> _currentOperations;

    /// <summary>
    /// Initializes a new instance of the <see cref="CombatState"/> class.
    /// </summary>
    /// <param name="client">The NosTale client.</param>
    /// <param name="game">The game.</param>
    /// <param name="combatManager">The combat manager.</param>
    public CombatState(ManagedNostaleClient client, Game.Game game, CombatManager combatManager)
    {
        Client = client;
        Game = game;
        CombatManager = combatManager;
        _operations = new Dictionary<OperationQueueType, LinkedList<ICombatOperation>>();
        _currentOperations = new Dictionary<OperationQueueType, ICombatOperation>();
    }

    /// <summary>
    /// Gets whether the combat state should be quit.
    /// </summary>
    public bool ShouldQuit { get; private set; }

    /// <inheritdoc/>
    public CombatManager CombatManager { get; }

    /// <inheritdoc/>
    public Game.Game Game { get; }

    /// <inheritdoc/>
    public ManagedNostaleClient Client { get; }

    /// <summary>
    /// Gets whether the manager may currently quit.
    /// </summary>
    /// <remarks>
    /// Used for finishing the current operations.
    /// </remarks>
    public bool CanQuit => _currentOperations.Values.All(x => !x.IsExecuting() || x.IsFinished());

    /// <inheritdoc/>
    public bool IsWaitingOnOperation => _currentOperations.Any(x => !x.Value.IsExecuting());

    /// <summary>
    /// Move to next operation, if available.
    /// </summary>
    /// <param name="queueType">The queue type to move to next operation in.</param>
    /// <returns>Next operation, if any.</returns>
    public ICombatOperation? NextOperation(OperationQueueType queueType)
    {
        if (_operations.ContainsKey(queueType))
        {
            var nextOperation = _operations[queueType].FirstOrDefault();

            if (nextOperation is not null)
            {
                _operations[queueType].RemoveFirst();
                _currentOperations[queueType] = nextOperation;
                return nextOperation;
            }
        }

        return null;
    }

    /// <inheritdoc/>
    public IReadOnlyList<ICombatOperation> GetWaitingForOperations()
    {
        return _currentOperations.Values.Where(x => !x.IsExecuting()).ToList();
    }

    /// <inheritdoc/>
    public ICombatOperation? RemoveCurrentOperation(OperationQueueType queueType, bool emptyQueue = false)
    {
        if (emptyQueue && _operations.TryGetValue(queueType, out var queue))
        {
            queue.Clear();
        }

        if (_currentOperations.TryGetValue(queueType, out var operation))
        {
            operation.Dispose();
            _currentOperations.Remove(queueType);
            return operation;
        }

        return null;
    }

    /// <inheritdoc/>
    public ICombatOperation? GetCurrentOperation(OperationQueueType queueType)
        => _currentOperations.GetValueOrDefault(queueType);

    /// <inheritdoc/>
    public bool IsExecutingOperation(OperationQueueType queueType, [NotNullWhen(true)] out ICombatOperation? operation)
    {
        operation = GetCurrentOperation(queueType);
        return operation is not null && operation.IsExecuting();
    }

    /// <inheritdoc/>
    public void QuitCombat()
    {
        ShouldQuit = true;
    }

    /// <inheritdoc/>
    public void SetCurrentOperation
        (ICombatOperation operation, bool emptyQueue = false, bool prependCurrentOperationToQueue = false)
    {
        var type = operation.QueueType;

        if (!_operations.ContainsKey(type))
        {
            _operations[type] = new LinkedList<ICombatOperation>();
        }

        if (emptyQueue)
        {
            _operations[type].Clear();
        }

        if (prependCurrentOperationToQueue)
        {
            _operations[type].AddFirst(operation);
            return;
        }

        if (_currentOperations.ContainsKey(type))
        {
            _currentOperations[type].Dispose();
        }

        _currentOperations[type] = operation;
    }

    /// <inheritdoc/>
    public void EnqueueOperation(ICombatOperation operation)
    {
        var type = operation.QueueType;

        if (!_operations.ContainsKey(type))
        {
            _operations[type] = new LinkedList<ICombatOperation>();
        }

        _operations[type].AddLast(operation);
    }

    /// <inheritdoc/>
    public void RemoveOperations(Func<ICombatOperation, bool> filter)
    {
        throw new NotImplementedException();
    }
}