using System;
using System.Collections;
using System.Collections.Generic;

using Syntriax.Engine.Core.Abstract;

namespace Syntriax.Engine.Core;

public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
{
    public event IAssignable.UnassignEventHandler? OnUnassigned = null;
    public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;

    public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
    public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;

    protected readonly List<T> behaviours = new(32);

    public IReadOnlyList<T> Behaviours => behaviours;
    public IGameManager GameManager { get; private set; } = null!;

    public T this[Index index] => behaviours[index];

    public BehaviourCollector() { }
    public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);

    private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
    {
        hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
        hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;

        foreach (IBehaviour item in hierarchyObject.BehaviourController)
            OnBehaviourAdded(hierarchyObject.BehaviourController, item);
    }

    private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
    {
        hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
        hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;

        foreach (IBehaviour item in hierarchyObject.BehaviourController)
            OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
    }

    protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
    private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
    {
        if (behaviour is not T tBehaviour)
            return;

        behaviours.Add(tBehaviour);
        OnBehaviourAdd(behaviour);
        OnCollected?.Invoke(this, tBehaviour);
    }

    protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
    private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
    {
        if (behaviour is not T tBehaviour)
            return;

        if (!behaviours.Remove(tBehaviour))
            return;

        OnBehaviourRemove(behaviour);
        OnRemoved?.Invoke(this, tBehaviour);
    }

    protected virtual void OnAssign(IGameManager gameManager) { }
    public bool Assign(IGameManager gameManager)
    {
        if (GameManager is not null)
            return false;

        foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
            OnHierarchyObjectRegistered(gameManager, hierarchyObject);

        gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
        gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;

        GameManager = gameManager;
        OnAssign(gameManager);
        OnGameManagerAssigned?.Invoke(this);

        return true;
    }

    public bool Unassign()
    {
        if (GameManager is null)
            return false;

        foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
            OnHierarchyObjectUnregistered(GameManager, hierarchyObject);

        GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
        GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;

        GameManager = null!;
        OnUnassigned?.Invoke(this);
        return true;
    }

    public IEnumerator<T> GetEnumerator() => behaviours.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
}