using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using System.Text.Json;

namespace RobotNet.WebApp.Maps.Models;

public class EdgeModel : IDisposable
{
    public Guid Id => Data.Id;
    public Guid MapId => Data.MapId;
    public double X1 => StartNode.X;
    public double Y1 => StartNode.Y;
    public double X2 => EndNode.X;
    public double Y2 => EndNode.Y;
    public DirectionAllowed DirectionAllowed => Data.DirectionAllowed;
    public TrajectoryDegree TrajectoryDegree => Data.TrajectoryDegree;
    public double ControlPoint1X => Data.ControlPoint1X;
    public double ControlPoint1Y => Data.ControlPoint1Y;
    public double ControlPoint2X => Data.ControlPoint2X;
    public double ControlPoint2Y => Data.ControlPoint2Y;
    public double MaxSpeed => Data.MaxSpeed;
    public double MaxHeight => Data.MaxHeight;
    public double MinHeight => Data.MinHeight;
    public bool RotationAllowed => Data.RotationAllowed;
    public double MaxRotationSpeed => Data.MaxRotationSpeed;
    public string Actions => Data.Actions;
    public double AllowedDeviationXy => Data.AllowedDeviationXy;
    public double AllowedDeviationTheta => Data.AllowedDeviationTheta;

    public bool ActivedControlPoint1 { get; set; }
    public bool ActivedControlPoint2 { get; set; }

    public NodeModel StartNode { get; private set; }
    public NodeModel EndNode { get; private set; }

    public event Func<Task>? StartNodePositionChanged;
    public event Func<Task>? EndNodePositionChanged;
    public event Func<Task>? ControlPoint1PositionChanged;
    public event Func<Task>? ControlPoint2PositionChanged;
    public event Func<Task>? DirectionChanged;

    public event Action<bool>? ActiveChanged;
    public event Action<EdgeModel>? SelectedChanged;
    public event Action<bool>? ErrorChanged;

    private readonly EdgeDto Data;
    public EdgeModel(EdgeDto data, NodeModel startNode, NodeModel endNode)
    {
        Data = data;
        StartNode = startNode;
        EndNode = endNode;

        StartNode.PositionChanged += StartNode_PositionChanged;
        EndNode.PositionChanged += EndNode_PositionChanged;
    }

    private void EndNode_PositionChanged() => EndNodePositionChanged?.Invoke();
    private void StartNode_PositionChanged() => StartNodePositionChanged?.Invoke();
    public void Active() => ActiveChanged?.Invoke(true);
    public void UnActive() => ActiveChanged?.Invoke(false);
    public void Selected() => SelectedChanged?.Invoke(this);

    public void UpdateNode(Guid id, NodeModel node)
    {
        if (StartNode.Id == id)
        {
            StartNode.PositionChanged -= StartNode_PositionChanged;
            StartNode = node;
            StartNode.PositionChanged += StartNode_PositionChanged;
            StartNodePositionChanged?.Invoke();
        }
        else if (EndNode.Id == id)
        {
            EndNode.PositionChanged -= EndNode_PositionChanged;
            EndNode = node;
            EndNode.PositionChanged += EndNode_PositionChanged;
            EndNodePositionChanged?.Invoke();
        }
    }

    public void UpdateData(EdgeUpdateModel model)
    {
        if(Data.ControlPoint1X != model.ControlPoint1X || Data.ControlPoint1Y != model.ControlPoint1Y)
        {
            Data.ControlPoint1X = model.ControlPoint1X;
            Data.ControlPoint1Y = model.ControlPoint1Y;
            ControlPoint1PositionChanged?.Invoke();
        }
        if (Data.ControlPoint2X != model.ControlPoint2X || Data.ControlPoint2Y != model.ControlPoint2Y)
        {
            Data.ControlPoint2X = model.ControlPoint2X;
            Data.ControlPoint2Y = model.ControlPoint2Y;
            ControlPoint2PositionChanged?.Invoke();
        }
        Data.RotationAllowed = model.RotationAllowed;
        Data.MaxSpeed = model.MaxSpeed;
        Data.MaxRotationSpeed = model.MaxRotationSpeed;
        Data.MaxHeight = model.MaxHeight;
        Data.MinHeight = model.MinHeight;
        Data.Actions = JsonSerializer.Serialize(model.Actions);

        if (Data.DirectionAllowed != model.DirectionAllowed)
        {
            Data.DirectionAllowed = model.DirectionAllowed;
            DirectionChanged?.Invoke();
        }
    }

    public void UpdateControlPoint1(double x, double y)
    {
        Data.ControlPoint1X = x;
        Data.ControlPoint1Y = y;
        ControlPoint1PositionChanged?.Invoke();
    }

    public void UpdateControlPoint2(double x, double y)
    {
        Data.ControlPoint2X = x;
        Data.ControlPoint2Y = y;
        ControlPoint2PositionChanged?.Invoke();
    }

    public void SetError(bool state) => ErrorChanged?.Invoke(state);

    public void Dispose()
    {
        StartNode.PositionChanged -= StartNode_PositionChanged;
        EndNode.PositionChanged -= EndNode_PositionChanged;
        GC.SuppressFinalize(this);
    }
}
