// Copyright © Selmo Technology GmbH. All Rights Reserved

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Novus.Connectivity.Common;
using SelmoStudio.Hmi.Common.SelmoProject.ProcessValue;
using SelmoStudio.Hmi.Plugin;
using SelmoStudio.Hmi.Server.ProcessValue.Services;
using SelmoStudio.Hmi.Server.SelmoHmi.Helpers;
using SelmoStudio.Hmi.Server.SelmoHmi.Model.Dtos;
using SelmoStudio.Hmi.Server.SelmoHmi.Model.Ids;
using SelmoStudio.Hmi.Server.SelmoHmi.ProjectInformation.Service;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component.CustomArgs;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component.Ids;
using System.Collections.Concurrent;
using System.Text;

namespace SelmoStudio.Hmi.Server.SelmoHmi.ManualControls.Components
{
    public partial class ManualControlsComponent : IBaseProcessValueComponent, IAsyncDisposable
    {
        private List<ZoneDto> _zones { get; set; } = new();
        private Dictionary<ProcessValueId, bool> _zoneProcessValues = new();
        private ConcurrentDictionary<ProcessValueId, bool> _activeInOutZoneProcessValues = new();
        private ConcurrentDictionary<ProcessValueId, bool> _activeOutZoneProcessValues = new();

        [Inject]
        private IProcessValueInfoService _processValueInfoService { get; set; }

        [Inject]
        private IProjectInformationService _projectInformationService { get; set; }

        [Inject]
        private IJSRuntime _jSRuntime { get; set; }

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

        [Parameter]
        public List<ZoneDto> Zones { get; set; }

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

        [Parameter]
        public bool ShowManualControls { get; set; }

        [Parameter]
        public Guid ParameterId { get; set; }

        [Parameter]
        public EventCallback<bool> BackClicked { get; set; }

        private string GetZoneText(ZoneDto zone)
        {
            if (!string.IsNullOrEmpty(zone.HmiButtonText))
            {
                return zone.HmiButtonText;
            }

            if (!string.IsNullOrEmpty(zone.Name))
            {
                return zone.Name;
            }

            return "";
        }

        protected override Task OnParametersSetAsync()
        {
            if (_zones != Zones)
            {
                _zones = Zones;
            }

            GenerateZoneProcessValuesLookup();
            GenerateActivatedZoneProcessValuesTracker(ZoneType.InOut, _activeInOutZoneProcessValues);
            GenerateActivatedZoneProcessValuesTracker(ZoneType.Out, _activeOutZoneProcessValues);

            return base.OnParametersSetAsync();
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            _isReady = true;
            base.OnAfterRender(firstRender);
        }

        private Dictionary<ZoneId, ProcessValuesZoneInfo> _lookUpTable;

        private void GenerateZoneProcessValuesLookup()
        {
            if (_lookUpTable == null)
                return;
            foreach (var processValue in _lookUpTable.Values)
            {
                _zoneProcessValues.TryAdd(processValue.ZoneManualButtonVariable.Id, false);
                _zoneProcessValues.TryAdd(processValue.ZoneInputActiveState.Id, false);
                _zoneProcessValues.TryAdd(processValue.ZoneOutputActiveState.Id, false);
            }
        }

        private void GenerateActivatedZoneProcessValuesTracker(ZoneType zoneType, ConcurrentDictionary<ProcessValueId, bool> trackingDictionary)
        {
            foreach (var zone in _zones)
            {
                if (GetZoneType(zone.ZoneType) == zoneType)
                {
                    var processValue = GetValueInfo(zone.Id);

                    if (processValue != null)
                    {
                        trackingDictionary.TryAdd(processValue.ZoneManualButtonVariable.Id, false);
                    }
                }
            }
        }

        private bool _isReady;

        public bool IsRendered
        {
            get
            {
                return _isReady;
            }
        }

        #region Handle ProcessValue Get/Set

        private ProcessValuesZoneInfo GetValueInfo(ZoneId id)
        {
            if (_lookUpTable == null)
                return null;
            if (_lookUpTable.ContainsKey(id))
            {
                return _lookUpTable[id];
            }
            return null;
        }

        public async ValueTask DisposeAsync()
        {
        }

        #endregion Handle ProcessValue Get/Set

        #region ComponentHandling

        private void AddEndComponents()
        {
            //TODO
        }

        private Dictionary<ComponentId, List<IProcessValue>> ExecuteableComponents;
        private List<IBaseProcessValueComponent> SubComponents;

        public event EventHandler<ComponentEventArgs> ComponentChangeEvent;

        public List<IProcessValueInfo> GetValueInfos()
        {
            var values = _processValueInfoService
             .CreateZoneHMIButtonRegistration(_zones.Select(z => z.Id).ToList(),
             _projectInformationService.CurrentSequence.SequenceInfo.Id).ValueInfos;

            _lookUpTable = _processValueInfoService.
                GetZoneLookupTable(_zones.Select(z => z.Id).ToList(),
                _projectInformationService.CurrentSequence.SequenceInfo.Id);

            return values.ToList();
        }

        public void Update(IDictionary<ProcessValueId, IProcessValue> updatedValues)
        {
            if (!IsRendered)
                return;
            UpdateProcessValues(updatedValues);
        }

        private void UpdateProcessValues(IDictionary<ProcessValueId, IProcessValue> processValues)
        {
            foreach (var zoneValue in _lookUpTable)
            {
                var value = zoneValue.Value;
                var idInput = value.ZoneInputActiveState.Id;
                var idOutput = value.ZoneOutputActiveState.Id;

                if (processValues.ContainsKey(idOutput))
                {
                    var foundValue = processValues[idOutput].Value;
                    bool castValue = (bool)foundValue;
                    if (!_zoneProcessValues.ContainsKey(idOutput))
                    {
                        _zoneProcessValues.Add(idOutput, castValue);
                    }
                    else
                    {
                        _zoneProcessValues[idOutput] = castValue;
                    }
                }

                if (processValues.ContainsKey(idInput))
                {
                    var foundValue = processValues[idInput].Value;
                    bool castValue = (bool)foundValue;
                    if (!_zoneProcessValues.ContainsKey(idInput))
                    {
                        _zoneProcessValues.Add(idInput, castValue);
                    }
                    else
                    {
                        _zoneProcessValues[idInput] = castValue;
                    }
                }
            }
        }

        public void TriggerComponentChangeEvent(ButtonEventArgs e, ZoneId zoneId, ZoneType zoneType, bool val)
        {
            if (e == null)
                return;

            var processValue = GetValueInfo(zoneId);
            if (processValue == null)
                return;

            if (e.EventType == Shared.Component.Types.EventType.MouseDown)
            {
                HandleActivatedZoneProcessValuesTracker(processValue.ZoneManualButtonVariable.Id, zoneType);
            }

            var handle = new ProcessValueHandleInfo()
            {
                ProcessValueHandle = ProcessValueHandle.SetValue,
                ProcessValueInfo = processValue.ZoneManualButtonVariable,
                Value = val
            };

            ComponentChangeEvent?.Invoke(this, ComponentEventArgs.Create(e, handle));
        }

        #endregion ComponentHandling

        private void HandleActivatedZoneProcessValuesTracker(ProcessValueId processValueId, ZoneType zoneType)
        {
            if (zoneType == ZoneType.InOut && _activeInOutZoneProcessValues.TryGetValue(processValueId, out var _activatedInOut))
            {
                _activeInOutZoneProcessValues[processValueId] = !_activatedInOut;
            }
            else if (zoneType == ZoneType.Out && _activeOutZoneProcessValues.TryGetValue(processValueId, out var _activatedOutput))
            {
                _activeOutZoneProcessValues[processValueId] = !_activatedOutput;
            }
        }

        private ZoneType GetZoneType(int zoneType)
        {
            if (Enum.IsDefined(typeof(ZoneType), zoneType))
            {
                return (ZoneType)zoneType;
            }

            throw new ArgumentException($"{zoneType} is not a valid Zone type value");
        }

        private string GetManualControlButtonStyle(ZoneId zoneId, ZoneType zoneType)
        {
            var zoneinfo = GetValueInfo(zoneId);

            if (zoneinfo == null || zoneType == ZoneType.In)
            {
                return string.Empty;
            }

            var styleString = new StringBuilder("background-color: ");

            bool inputActive = false;
            bool outputActive = false;

            if (zoneType == ZoneType.InOut)
            {
                _zoneProcessValues.TryGetValue(zoneinfo.ZoneInputActiveState.Id, out inputActive);
                _zoneProcessValues.TryGetValue(zoneinfo.ZoneOutputActiveState.Id, out outputActive);
            }
            else if (zoneType == ZoneType.Out)
            {
                _zoneProcessValues.TryGetValue(zoneinfo.ZoneOutputActiveState.Id, out outputActive);
            }

            var inOutZoneButtonColor = (inputActive || outputActive)
                            ? HtmlStyleHelper.GetActiveInOutZoneButtonBackgroundColor()
                            : HtmlStyleHelper.GetToggleButtonBackgroundColor(false);

            styleString.Append(inOutZoneButtonColor);
            styleString.Append(';');

            return styleString.ToString();
        }
    }
}