@inject IJSRuntime JSRuntime
@implements IDisposable

<path @ref="ErrorRef" style="stroke: red; opacity: 0.6; stroke-width: 0.6px;" />
<path @ref="Ref" @ondblclick="@(async () => await DoubleClick.InvokeAsync(Model))" class="@(IsSetting ? "setting" : "")" />

@code {
    [Parameter, EditorRequired]
    public EdgeModel Model { get; set; } = null!;

    [Parameter]
    public EventCallback<EdgeModel> DoubleClick { get; set; }

    [CascadingParameter]
    protected bool MapIsActive { get; set; }

    [Parameter]
    public EditorState EditorState { get; set; }

    private ElementReference Ref;
    private ElementReference ErrorRef;
    private DotNetObjectReference<Edge> DotNetObj = null!;
    private bool IsError = false;

    private bool IsSetting => EditorState == EditorState.NavigationEdit || EditorState == EditorState.CreateStraighEdge || EditorState == EditorState.CreateCurveEdge || 
                              EditorState == EditorState.CreateDoubleCurveEdge;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);
        if (!firstRender) return;

        DotNetObj = DotNetObjectReference.Create(this);
        await JSRuntime.InvokeVoidAsync("AddEventListener", DotNetObj, Ref, "click", nameof(Click), false);
        await UpdatePathData();
        await JSRuntime.InvokeVoidAsync("ElementSetAttribute", ErrorRef, "visibility", "hidden");
    }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        bool updateLine = false;
        if (parameters.TryGetValue(nameof(Model), out EdgeModel? model))
        {
            if (((Model?.Id ?? Guid.Empty) != (model?.Id ?? Guid.Empty)))
            {
                if (Model != null)
                {
                    Model.StartNodePositionChanged -= UpdatePathData;
                    Model.EndNodePositionChanged -= UpdatePathData;
                    Model.ControlPoint1PositionChanged -= UpdatePathData;
                    Model.ControlPoint2PositionChanged -= UpdatePathData;
                    Model.ActiveChanged -= ActivedChanged;
                    Model.ErrorChanged -= ErrorChanged;
                }

                if (model != null)
                {
                    model.StartNodePositionChanged += UpdatePathData;
                    model.EndNodePositionChanged += UpdatePathData;
                    model.ControlPoint1PositionChanged += UpdatePathData;
                    model.ControlPoint2PositionChanged += UpdatePathData;
                    model.ActiveChanged += ActivedChanged;
                    model.ErrorChanged += ErrorChanged;

                    updateLine = true;
                }
            }
        }

        await base.SetParametersAsync(parameters);
        if (updateLine) await UpdatePathData();
    }

    private async Task UpdatePathData()
    {
        var data = $"M {Model.X1} {Model.Y1}";

        if (Model.TrajectoryDegree == TrajectoryDegree.One)
        {
            data = $"{data} L {(Model.X1 + Model.X2) / 2} {(Model.Y1 + Model.Y2) / 2} L {Model.X2} {Model.Y2}";
        }
        else if (Model.TrajectoryDegree == TrajectoryDegree.Two)
        {
            data = $"{data} Q {Model.ControlPoint1X} {Model.ControlPoint1Y} {Model.X2} {Model.Y2}";
        }
        else if (Model.TrajectoryDegree == TrajectoryDegree.Three)
        {
            data = $"{data} C {Model.ControlPoint1X} {Model.ControlPoint1Y}, {Model.ControlPoint2X} {Model.ControlPoint2Y}, {Model.X2} {Model.Y2}";
        }

        await JSRuntime.InvokeVoidAsync("ElementSetAttribute", Ref, "d", data);
        if (IsError) await JSRuntime.InvokeVoidAsync("ElementSetAttribute", ErrorRef, "d", data);
    }

    private async void ActivedChanged(bool state) => await JSRuntime.InvokeVoidAsync(state ? "AddSelected" : "RemoveSelected", Ref);

    private async void ErrorChanged(bool state)
    {
        IsError = state;
        await JSRuntime.InvokeVoidAsync("ElementSetAttribute", ErrorRef, "visibility", state ? "visible" : "hidden");
        await UpdatePathData();
    }

    [JSInvokable]
    public void Click() => Model.Selected();

    public void Dispose()
    {
        if (Model != null)
        {
            Model.StartNodePositionChanged -= UpdatePathData;
            Model.EndNodePositionChanged -= UpdatePathData;
            Model.ControlPoint1PositionChanged -= UpdatePathData;
            Model.ControlPoint2PositionChanged -= UpdatePathData;
            Model.ActiveChanged -= ActivedChanged;
            Model.ErrorChanged -= ErrorChanged;
        }
        if (DotNetObj != null) DotNetObj.Dispose();
        GC.SuppressFinalize(this);
    }
}
