// Copyright © Selmo Technology GmbH. All Rights Reserved

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using SelmoStudio.Hmi.Server.Shared.Components.Chart.Interfaces;
using SelmoStudio.Hmi.Server.Shared.Components.Chart.Models.Common;
using System.Threading.Tasks;

namespace SelmoStudio.Hmi.Server.Shared.Components.Chart
{
    /// <summary>
    /// Class Chart. Implements the <see cref="ComponentBase"/> Implements the <see cref="IDisposable"/>
    /// </summary>
    /// <seealso cref="ComponentBase"/>
    /// <seealso cref="IDisposable"/>
    public partial class Chart : IDisposable
    {
        #region .NET Object references

        /// <summary>
        /// Gets or sets the js module.
        /// </summary>
        /// <value>The js module.</value>
        protected ChartJsInterop? JSModule { get; set; }

        #endregion .NET Object references

        #region Parameters

        [Parameter]
        public string Id { get; set; }

        /// <summary>
        /// Gets or sets the class.
        /// </summary>
        /// <value>The class.</value>
        [Parameter]
        public string? Class { get; set; }

        /// <summary>
        /// Gets or sets the configuration.
        /// </summary>
        /// <value>The configuration.</value>
        [Parameter]
        public IChartConfig Config { get; set; }

        /// <summary>
        /// Gets or sets the height.
        /// </summary>
        /// <value>The height.</value>
        [Parameter]
        public string? Height { get; set; }

        /// <summary>
        /// Gets or sets the old configuration.
        /// </summary>
        /// <value>The old configuration.</value>
        public IChartConfig OldConfig { get; set; }

        /// <summary>
        /// Gets or sets the style.
        /// </summary>
        /// <value>The style.</value>
        [Parameter]
        public string? Style { get; set; }

        /// <summary>
        /// Gets or sets the width.
        /// </summary>
        /// <value>The width.</value>
        [Parameter]
        public string? Width { get; set; }

        #region Events

        /// <summary>
        /// Gets or sets the on chart click.
        /// </summary>
        /// <value>The on chart click.</value>
        [Parameter]
        public EventCallback<CallbackGenericContext> OnChartClick { get; set; }

        /// <summary>
        /// Gets or sets the on chart over.
        /// </summary>
        /// <value>The on chart over.</value>
        [Parameter]
        public EventCallback<HoverContext> OnChartOver { get; set; }

        /// <summary>
        /// Gets or sets the on legend click.
        /// </summary>
        /// <value>The on legend click.</value>
        [Parameter]
        public EventCallback<LegendClickContext> OnLegendClick { get; set; }

        #endregion Events

        #region Public functions

        public async void AddData(List<string?> labels, int datasetIndex, List<decimal?> data)
        {
            await JSModule.AddData(Config.CanvasId, labels, datasetIndex, data);
        }

        public async void AddData(int datasetIndex, decimal x, decimal y)
        {
            await JSModule.AddData(Config.CanvasId, datasetIndex, x, y);
        }

        public async void AddDataWithRemoveFirst(List<string?> labels, List<decimal?> data)
        {
            await JSModule.AddDataWithRemoveFirst(Config.CanvasId, labels, data);
        }

        public async void RemoveFirst(int datasetIndex)
        {
            await JSModule.RemoveFirst(Config.CanvasId, datasetIndex);
        }

        public async void AddDataset<T>(T dataset) where T : class
        {
            await JSModule.AddNewDataset(Config.CanvasId, dataset);
        }

        public async void RemoveDataset(int datasetIndex)
        {
            await JSModule.RemoveDataset(Config.CanvasId, datasetIndex);
        }

        public async void UpdateXScale(decimal? min, decimal? max)
        {
            await JSModule.UpdateXScale(Config.CanvasId, min, max);
        }

        #endregion Public functions

        #endregion Parameters

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (Config != null)
            {
                if (OldConfig == null || Config != OldConfig)
                {
                    var dotNetObjectRef = DotNetObjectReference.Create(Config);

                    JSModule = new ChartJsInterop(JSRuntime);
                    await JSModule.Setup(dotNetObjectRef, Config);
                }

                OldConfig = Config;
            }
        }

        private ValueTask OnMouseOutAsync(MouseEventArgs mouseEventArgs)
        {
            if (Config.Options is Options { OnMouseOutAsync: { } } options)
                return options.OnMouseOutAsync(mouseEventArgs);
            else
                return ValueTask.CompletedTask;
        }

        #region JavaScript invokable functions

        [JSInvokable]
        public static string[] TitleCallbacks(DotNetObjectReference<IChartConfig> config, decimal[] parameters)
        {
            var ctx = new CallbackGenericContext((int)parameters[0], (int)parameters[1], parameters[2]);
            if (config.Value.Options is Options options)
                return options.Plugins.Tooltip.Callbacks.Title(ctx);
            else
                throw new NotSupportedException();
        }

        [JSInvokable]
        public static string[] TooltipCallbacksLabel(DotNetObjectReference<IChartConfig> config, int[] parameters)
        {
            var ctx = new CallbackGenericContext(parameters[0], parameters[1], parameters[2]);
            if (config.Value.Options is Options options)
                return options.Plugins.Tooltip.Callbacks.Label(ctx);
            else
                throw new NotSupportedException();
        }

        [JSInvokable]
        public static async Task<ValueTask> OnClickAsync(DotNetObjectReference<IChartConfig> config, CallbackGenericContext ctx)
        {
            //await OnChartClick.InvokeAsync(ctx);

            if (config.Value.Options is Options options && options.OnClickAsync != null)
                return options.OnClickAsync(ctx);
            else
                return ValueTask.CompletedTask;
        }

        [JSInvokable]
        public static async Task<ValueTask> OnHoverAsync(DotNetObjectReference<IChartConfig> config, HoverContext ctx)
        {
            if (config.Value.Options is Options options && options.OnHoverAsync != null)
                return options.OnHoverAsync(ctx);
            else
                return ValueTask.CompletedTask;
        }

        [JSInvokable]
        public static async Task<ValueTask> OnLegendClickAsync(DotNetObjectReference<IChartConfig> config, LegendClickContext ctx)
        {
            if (config.Value.Options is Options options && options?.Plugins?.Legend?.OnClickAsync != null)
                return options.Plugins.Legend.OnClickAsync(ctx);
            else
                return ValueTask.CompletedTask;
        }

        #endregion JavaScript invokable functions

        public void Dispose()
        {
        }
    }
}