using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;

namespace RobotNet.WebApp.Maps.Models;

public class MapEdgeModel : IEnumerable<EdgeModel>
{
    private readonly Dictionary<Guid, EdgeModel> dicEdges = [];
    public EdgeModel this[int i] => dicEdges.Values.ElementAt(i);
    public EdgeModel this[Guid id] => dicEdges[id];
    public IEnumerator<EdgeModel> GetEnumerator() => dicEdges.Values.GetEnumerator();
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
    public bool TryGetValue(Guid id, out EdgeModel? value) => dicEdges.TryGetValue(id, out value);
    public bool ContainsKey(Guid key) => dicEdges.ContainsKey(key);

    public List<EdgeModel> ActivedEdges { get; private set; } = [];

    public event Action? Changed;
    public event Action<EdgeModel?>? EdgeSelectedChanged;
    private (double X, double Y) StartMovePosition;

    public void Add(EdgeDto edge, NodeModel start, NodeModel end)
    {
        if (dicEdges.ContainsKey(edge.Id)) return;
        var model = new EdgeModel(edge, start, end);
        dicEdges.Add(model.Id, model);
        model.SelectedChanged += EdgeSelectedChange;
        Changed?.Invoke();
    }

    public void ReplaceAll(IEnumerable<EdgeModel> edges)
    {
        foreach (var edge in dicEdges.Values)
        {
            if (ActivedEdges.Any(e => e.Id == edge.Id))
            {
                edge.SelectedChanged -= EdgeSelectedChange;
                edge.UnActive();
                ActivedEdges.Remove(edge);
            }
            edge.Dispose();
        }
        dicEdges.Clear();

        foreach (var edge in edges)
        {
            dicEdges.Add(edge.Id, edge);
            edge.SelectedChanged += EdgeSelectedChange;
        }
        Changed?.Invoke();
    }

    public void Remove(EdgeModel edge)
    {
        if (!dicEdges.ContainsKey(edge.Id)) return;

        edge.SelectedChanged -= EdgeSelectedChange;
        if (ActivedEdges.Any(e => e.Id == edge.Id))
        {
            ActivedEdges.Remove(edge);
        }
        edge.Dispose();

        dicEdges.Remove(edge.Id);
        Changed?.Invoke();
    }

    private void EdgeSelectedChange(EdgeModel edge)
    {
        EdgeSelectedChanged?.Invoke(edge);
    }

    public void UnActivedEdge(EdgeModel? exceptedModel = null)
    {
        foreach (var edge in ActivedEdges)
        {
            if (exceptedModel is not null && edge.Id == exceptedModel.Id) continue;
            var edgeInDic = dicEdges.Values.FirstOrDefault(e => e.Id.Equals(edge.Id));
            edgeInDic?.UnActive();
        }
        ActivedEdges.Clear();
        if (exceptedModel is not null) ActivedEdges.Add(exceptedModel);
    }

    public void ActivedEdge(IEnumerable<EdgeModel> edges)
    {
        UnActivedEdge();
        foreach (var edge in edges)
        {
            var edgeInDic = dicEdges[edge.Id];
            edgeInDic?.Active();
            ActivedEdges.Add(edge);
        }
    }

    public void SetStartMovePosition(double x, double y)
    {
        StartMovePosition.X = x;
        StartMovePosition.Y = y;
    }

    public void UpdateMoveEdge(double x, double y)
    {
        double deltaX = x - StartMovePosition.X;
        double deltaY = y - StartMovePosition.Y;
        List<Guid> NodeUpdated = [];
        foreach (var edge in ActivedEdges)
        {
            if (!NodeUpdated.Any(n => n == edge.StartNode.Id)) edge.StartNode.UpdatePosition(edge.StartNode.X + deltaX, edge.StartNode.Y + deltaY);
            if (!NodeUpdated.Any(n => n == edge.EndNode.Id)) edge.EndNode.UpdatePosition(edge.EndNode.X + deltaX, edge.EndNode.Y + deltaY);
            if (edge.TrajectoryDegree == TrajectoryDegree.Two) edge.UpdateControlPoint1(edge.ControlPoint1X + deltaX, edge.ControlPoint1Y + deltaY);
            if (edge.TrajectoryDegree == TrajectoryDegree.Three)
            {
                edge.UpdateControlPoint1(edge.ControlPoint1X + deltaX, edge.ControlPoint1Y + deltaY);
                edge.UpdateControlPoint2(edge.ControlPoint2X + deltaX, edge.ControlPoint2Y + deltaY);
            }
            NodeUpdated.Add(edge.StartNode.Id);
            NodeUpdated.Add(edge.EndNode.Id);
        }
        StartMovePosition.X = x;
        StartMovePosition.Y = y;
    }
}
