@using Microsoft.JSInterop
@using System.Reflection;
@using System.ComponentModel
@inject IJSRuntime JSRuntime
@implements IDisposable
<div class=@WrapperClass>
    <div @ref="dialog" class=@Class role="dialog" aria-labelledby="rz-dialog-0-label" style=@Style>
        @if (Dialog?.Options?.ShowTitle == true)
        {
            <div class="rz-dialog-titlebar">
                <div class="rz-dialog-title" id="rz-dialog-0-label">
                    @if (!string.IsNullOrEmpty(Dialog.Options.Icon))
                    {
                        <RadzenIcon Icon="@Dialog.Options.Icon" 
                                    IconColor="@(string.IsNullOrEmpty(Dialog.Options.IconColor) ? null : Dialog.Options.IconColor)" 
                                    Style="@Dialog.Options.IconStyle" />
                    }
                    @if (Dialog.Options.TitleContent != null && Service != null)
                    {
                        @Dialog.Options.TitleContent(Service)
                    }
                    else
                    {
                        @Dialog.Title
                    }
                </div>
                @if (Dialog.Options.ShowClose)
                {
                    <a @onclick:preventDefault="true" @onclick=@Close @onkeydown="@OnKeyPress" role="button" class="rz-dialog-titlebar-icon rz-dialog-titlebar-close" tabindex=@Dialog.Options.CloseTabIndex>
                        <span class="notranslate rzi rzi-times"></span>
                    </a>
                }
            </div>
        }
        <div class=@ContentClass>
            @if (Dialog?.Options?.ChildContent != null && Service != null)
            {
                @Dialog.Options.ChildContent(Service)
            }
            else
            {
                @ChildContent
            }
        </div>
    </div>

    @if (ShowMask)
    {
        @if (Dialog?.Options?.CloseDialogOnOverlayClick == true)
        {
            <div @onclick="@Close" class="rz-dialog-mask"></div>
        }
        else
        {
            <div class="rz-dialog-mask" style="pointer-events: none;"></div>
        }
    }
</div>

@code {
    void OnKeyPress(KeyboardEventArgs args)
    {
        var key = args.Code != null ? args.Code : args.Key;

        if (key == "Space" || key == "Enter")
        {
            Close();
        }
    }

    ElementReference dialog;

    string? left;
    string? top;
    string? height;
    string? width;

    /// <summary>
    /// Called when dragged.
    /// </summary>
    /// <param name="t">The top value.</param>
    /// <param name="l">The left value.</param>
    [JSInvokable("RadzenDialog.OnDrag")]
    public void OnDrag(double t, double l)
    {
        shouldRender = false;

        if (Dialog?.Options?.Drag != null)
        {
            Dialog.Options.Drag(new System.Drawing.Point(Convert.ToInt32(l), Convert.ToInt32(t)));
        }

        top = $"top: {t}px;";
        left = $"left: {l}px;";

        shouldRender = true;
    }


    /// <summary>
    /// Called when resized.
    /// </summary>
    /// <param name="w">The width.</param>
    /// <param name="h">The height.</param>
    [JSInvokable("RadzenDialog.OnResize")]
    public void OnResize(double w, double h)
    {
        shouldRender = false;

        if(Dialog?.Options?.Resize != null && w != 0 && h != 0)
        {
            Dialog.Options.Resize(new System.Drawing.Size(Convert.ToInt32(w), Convert.ToInt32(h)));
        }

        width = $"width: {w}px;";
        height = $"height: {h}px;";

        shouldRender = true;
    }

    bool shouldRender = true;
    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    [Parameter]
    public Dialog? Dialog { get; set; }

    [Parameter]
    public bool ShowMask { get; set; } = true;

    RenderFragment ChildContent => new RenderFragment(builder =>
    {
        // Wrap the Dialog component in a CascadingValue

        builder.OpenComponent<CascadingValue<Dialog>>(0); // Open CascadingValue
        builder.AddAttribute(1, "Value", Dialog);
        builder.AddAttribute(2, "IsFixed", true);
        builder.AddAttribute(3, "ChildContent", (RenderFragment)((builder2) =>
        {
            if (Dialog != null && Dialog.Type != null)
            {
                builder2.OpenComponent(0, Dialog.Type); // Open Dialog

                if (Dialog.Parameters != null)
                {
                    foreach (var parameter in Dialog.Parameters)
                    {
                        builder2.AddAttribute(1, parameter.Key, parameter.Value);
                    }
                }

                builder2.AddComponentReferenceCapture(2, component => reference = component); // Capture reference
                builder2.CloseComponent(); // Close Dialog
            }
        }));
        builder.CloseComponent(); // Close CascadingValue
    });

    object? reference;

    void Close()
    {
        Service?.Close();
    }

    string Class => ClassList.Create("rz-dialog rz-open")
                             .Add(Dialog?.Options?.CssClass)
                             .ToString();

    string WrapperClass => ClassList.Create("rz-dialog-wrapper")
                                    .Add(Dialog?.Options?.WrapperCssClass)
                                    .ToString();

    string ContentClass => ClassList.Create("rz-dialog-content")
                                    .Add(Dialog?.Options?.ContentCssClass)
                                    .ToString();

    string Style
    {
        get
        {
            var baseStyle = "";
            var widthStyle = !string.IsNullOrEmpty(width) ? width : string.IsNullOrEmpty(Dialog?.Options?.Width) ? "" : $"width: {Dialog?.Options?.Width};";
            var heightStyle = !string.IsNullOrEmpty(height) ? height : string.IsNullOrEmpty(Dialog?.Options?.Height) ? "" : $"height: {Dialog?.Options?.Height};";
            var topStyle = !string.IsNullOrEmpty(top) ? top : string.IsNullOrEmpty(Dialog?.Options?.Top) ? "" : $"top: {Dialog?.Options?.Top};";
            var leftStyle = !string.IsNullOrEmpty(left) ? left :string.IsNullOrEmpty(Dialog?.Options?.Left) ? "" : $"left: {Dialog?.Options?.Left};";
            var bottomStyle = string.IsNullOrEmpty(Dialog?.Options?.Bottom) ? "" : $"bottom: {Dialog.Options.Bottom};";
            var resizeStyle = Dialog?.Options?.Resizable == true ? "resize:both;" : "";

            return $"{baseStyle}{widthStyle}{heightStyle}{topStyle}{leftStyle}{bottomStyle}{resizeStyle}{Dialog?.Options?.Style ?? ""}";
        }
    }

    [Inject] 
    DialogService? Service { get; set; }

    protected override void OnInitialized()
    {
        if (Service != null)
        {
            Service.OnRefresh += OnRefresh;
        }

		// Subscribe to PropertyChanged event to auto refresh the dialog when properties change
        if (Dialog != null)
        {
            Dialog.PropertyChanged += OnPropertyChanged;
        }

		// Subscribe to PropertyChanged event to auto refresh the dialog when properties change
		if (Dialog?.Options != null)
			Dialog.Options.PropertyChanged += OnPropertyChanged;
    }

    /// <summary>
    /// Handles the property changed event and triggers a refresh.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
    void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        OnRefresh();
    }

    void OnRefresh()
    {
        var stateHasChanged = reference?.GetType().GetMethod("StateHasChanged", BindingFlags.NonPublic | BindingFlags.Instance);

        InvokeAsync(() => 
        {
            StateHasChanged();
            stateHasChanged?.Invoke(reference, null);
        })
        .ConfigureAwait(false);
    }

    public void Dispose()
    {
        if (Service != null)
        {
            Service.OnRefresh -= OnRefresh;
        }

		// Unsubscribe from PropertyChanged event
        if (Dialog != null)
        {
            Dialog.PropertyChanged -= OnPropertyChanged;
        }

		// Unsubscribe from PropertyChanged event
        if (Dialog?.Options != null)
            Dialog.Options.PropertyChanged -= OnPropertyChanged;

    }

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

        if (firstRender)
        {
            var options = Dialog?.Options;

            var dialogOptions = new DialogOptions()
                {
                    Width = options != null ? !string.IsNullOrEmpty(options.Width) ? options.Width : "" : "",
                    Height = options != null ? options.Height : null,
                    Left = options != null ? options.Left : null,
                    Top = options != null ? options.Top : null,
                    Bottom = options != null ? options.Bottom : null,
                    ShowTitle = options != null ? options.ShowTitle : true,
                    ShowClose = options != null ? options.ShowClose : true,
                    Resizable = options != null ? options.Resizable : false,
                    Draggable = options != null ? options.Draggable : false,
                    Style = options != null ? options.Style : "",
                    AutoFocusFirstElement = options != null ? options.AutoFocusFirstElement : true,
                    CloseDialogOnOverlayClick = options != null ? options.CloseDialogOnOverlayClick : false,
                    CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
                    CssClass = options != null ? options.CssClass : "",
                    CloseTabIndex = options != null ? options.CloseTabIndex : 0,
                };

            if (Service?.Reference != null)
            {
                await JSRuntime.InvokeAsync<string>("Radzen.openDialog", dialogOptions, Service.Reference, DotNetReference);
            }
        }
    }

    private DotNetObjectReference<DialogContainer>? dotNetReference;

    /// <summary>
    /// Gets the reference for the current component.
    /// </summary>
    /// <value>The reference.</value>
    protected DotNetObjectReference<DialogContainer> DotNetReference
    {
        get
        {
            if (dotNetReference == null)
            {
                dotNetReference = DotNetObjectReference.Create(this);
            }

            return dotNetReference;
        }
    }
}