// 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.Server.Pagination;
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.Plant.Components;
using SelmoStudio.Hmi.Server.SelmoHmi.ProjectInformation.Service;
using SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Components;
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.Mappers;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Model;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Service;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.WellKnownTypes;
using SelmoStudio.Hmi.Server.Service;

namespace SelmoStudio.Hmi.Server.SelmoHmi.Plant.Pages
{
    public partial class PlantOverview : IBaseProcessValueComponent, IAsyncDisposable
    {
        [Inject]
        private ILogger<PlantOverview> _logger { get; set; }

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

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

        private ProcessValuesHardwareZone _hwZoneValues;

        private Dictionary<Guid, bool> _boolProcessValues = new();

        private bool _automaticMode = true;

        private int _calculatedPageSize = 3;

        private PaginationCache<HardwareZoneInfoDto> _hardwareZonePagination;

        private HardwareZoneInfoDto[] _currentPagination;

        private bool _paginationIsActive => _hardwareZonePagination.HasMoreThanOnePage();

        private IEnumerable<HardwareZoneInfoDto> _hardwareZoneInfos;

        private DotNetObjectReference<PlantOverview> _viewportReference;

        private bool _isDisposed;

        private Lazy<Task<IJSObjectReference>> _resizeCallbackRef;

        [Inject]
        private IHmiLanguageService _hmiLanguageService { get; set; }

        [Inject]
        private IBrowserService _browserService { get; set; }

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

        [JSInvokable]
        public async Task OnResize()
        {
            if (!_isDisposed)
            {
                await SetVisibleHardwareZones();
            }
        }

        #region Init_Lifecycle

        protected override async Task OnInitializedAsync()
        {
            _logger.LogInformation("Loading Plant Overview");

            _hardwareZoneInfos = _projectInformationService.HardwareZoneInfos;

            InitializePagination();

            await base.OnInitializedAsync();
        }

        private bool _isRendered;

        public bool IsRendered
        {
            get
            {
                return true;
                foreach (var subcomponent in SubComponents)
                {
                    if (!subcomponent.IsRendered)
                        return false;
                }
                return _isRendered;
            }
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                _viewportReference = DotNetObjectReference.Create(this);
                _resizeCallbackRef = new(() => _jsRuntime.InvokeAsync<IJSObjectReference>(
                    "registerViewportChangeCallback", _viewportReference).AsTask());
            }

            UnsubscribeToAllComponents();
            AddSubComponents();
            SubscribeToAllSubPages();
            await base.OnAfterRenderAsync(firstRender);
            _isRendered = true;
        }

        protected override async Task OnParametersSetAsync()
        {
            Select(_hardwareZoneInfos.First());
            _showOverview = true;
            _showSequence = true;

            await SetVisibleHardwareZones();

            _hmiLanguageService.OnLanguageSet += OnLanguageSet;

            ResetBoolValues(_hwZoneValues);

            await base.OnParametersSetAsync();
        }

        #endregion Init_Lifecycle

        private async Task SetVisibleHardwareZones()
        {
            await CalculateVisibleHardwareZonesPageSize();

            _hardwareZonePagination.SetPageSize(_calculatedPageSize);
            _hardwareZonePagination.SelectItem(_projectInformationService.CurrentHardwareZone.HardwareZoneInfo);
            UpdateCurrentPagination();

            await InvokeAsync(() => StateHasChanged());
        }

        private void InitializePagination()
        {
            _hardwareZonePagination = new(_hardwareZoneInfos.ToList());
        }

        private void UpdateCurrentPagination()
        {
            _currentPagination = _hardwareZonePagination.GetCurrent();
        }

        private async Task CalculateVisibleHardwareZonesPageSize()
        {
            var dimensions = await _browserService.GetDimensions();
            _calculatedPageSize = ScreenSizeHelper.GetPageSize(dimensions.Width);
        }

        internal List<IProcessValueInfo> Registrations { get; private set; } = new();
        internal List<IProcessValueInfo> WriteValues { get; private set; } = new();

        private Dictionary<HardwareZoneId, List<IProcessValueInfo>> _lookUpTableForHwStatus;
        private Dictionary<ProcessValueId, IProcessValue> _lookUpTableForHwStatusValues = new();

        private void Select(HardwareZoneInfoDto hardwareZoneInfo)
        {
            HideAllPanels();
            _projectInformationService.SelectHardwareZone(hardwareZoneInfo);

            _showSequence = true;
            _showOverview = true;

            InvokeAsync(() => StateHasChanged());
        }

        private void OnNextClicked()
        {
            _hardwareZonePagination.GoToNextPage();
            UpdateCurrentPagination();
        }

        private void OnPreviousClicked()
        {
            _hardwareZonePagination.GoToPreviousPage();
            UpdateCurrentPagination();
        }

        private void HwBackClick()
        {
            HideAllPanels();
        }

        private void PlantBackClick()
        {
            HideAllPanels();
        }

        private void OnSequenceStateChanged(bool value)
        {
            _showOverview = value;
        }

        private void OnLanguageSet()
        {
            InvokeAsync(() => StateHasChanged());
        }

        #region Show / Hide Panels

        private bool _showGlobalOptions = false;
        private bool _showHwZoneParams = false;
        private bool _showPlantParams = false;
        private bool _showSequence = false;
        private bool _showOverview;

        private void InvertVisibleHwZoneParams()
        {
            HideAllPanels();
            _showHwZoneParams = !_showHwZoneParams;

            UnsubscribeToAllComponents();
            AddSubComponents();
            SubscribeToAllSubPages();
        }

        private void InvertVisiblePlantParams()
        {
            HideAllPanels();
            _showPlantParams = !_showPlantParams;

            UnsubscribeToAllComponents();
            AddSubComponents();
            SubscribeToAllSubPages();
        }

        private void HideAllPanels()
        {
            _showPlantParams = false;
            _showHwZoneParams = false;
        }

        private void InvertVisibleGlobalOptionsSidePanel()
        {
            _showGlobalOptions = !_showGlobalOptions;
        }

        #endregion Show / Hide Panels

        #region Content Style

        private WellKnownContentFlexDirection _contentFlexDirection = WellKnownContentFlexDirection.Column;

        #endregion Content Style

        public void Dispose()
        {
        }

        private void ResetBoolValues(ProcessValuesHardwareZone zoneValues)
        {
            if (zoneValues == null)
                return;

            _boolProcessValues = new()
            {
                {_hwZoneValues.AllSequencesReady.Id.Guid, false },
                {_hwZoneValues.AnySequencesReady.Id.Guid, false},
                {_hwZoneValues.AutomaticLamp.Id.Guid, false},
                {_hwZoneValues.AutomaticMode.Id.Guid, false},
                {_hwZoneValues.EocMode.Id.Guid, false},
                {_hwZoneValues.EocModeIsOn.Id.Guid, false},
                {_hwZoneValues.EocModeIsOff.Id.Guid, false},
                {_hwZoneValues.ManualMode.Id.Guid, false},
                {_hwZoneValues.EmergencyStopLamp.Id.Guid, false},
                {_hwZoneValues.EocReachedLamp.Id.Guid, false},
                {_hwZoneValues.HardwareZoneAutomaticRelease.Id.Guid, false},
                {_hwZoneValues.AllSequencesAutomaticRelease.Id.Guid, false},
                {_hwZoneValues.AnySeqInterlockFault.Id.Guid, false},
                {_hwZoneValues.AnySeqCmzFault.Id.Guid, false}
            };
        }

        private bool GetCurrentBoolValueForProcessValue(Guid id)
        {
            bool zoneValue = false;
            _boolProcessValues.TryGetValue(id, out zoneValue);
            return zoneValue;
        }

        private bool IsCurrentlyManualMode()
        {
            if (_hwZoneValues == null)
                return true;

            var manModeId = _hwZoneValues.ManualMode.Id;

            if (_lookUpTableForHwStatusValues.ContainsKey(manModeId))
            {
                var isManMode = (bool)_lookUpTableForHwStatusValues[manModeId].Value;
                return isManMode;
            }

            return true;
        }

        private List<string> GetIconList(HardwareZoneInfoDto hw)
        {
            var iconList = new List<IProcessValueInfo>();
            if (_lookUpTableForHwStatus == null)
                return StatusToIconMapper.MapFromProcessValues(iconList);
            foreach (var info in _lookUpTableForHwStatus[hw.Id])
            {
                if (_lookUpTableForHwStatusValues.ContainsKey(info.Id))
                {
                    var value = (bool)_lookUpTableForHwStatusValues[info.Id].Value;
                    if (value)
                        iconList.Add(info);
                }
            }
            return StatusToIconMapper.MapFromProcessValues(iconList);
        }

        private OperationMode GetLedStatus(HardwareZoneInfoDto hw)
        {
            if (_lookUpTableForHwStatus == null || _lookUpTableForHwStatus.Count <= 0)
                return OperationMode.Error;
            return StatusHelper.GetOperationMode(_hwZoneValues, _lookUpTableForHwStatusValues);
            //if (_lookUpTableForHwStatus == null)
            //    return OperationMode.Error;

            //var infos = _lookUpTableForHwStatus[hw.Id];
            //var manualControl = infos.First(info => info.Name.Contains("Manual"));

            //if (!_lookUpTableForHwStatusValues.ContainsKey(manualControl))
            //    return OperationMode.Error;

            //var manualValue = (bool)_lookUpTableForHwStatusValues[manualControl].Value;

            //if (manualValue)
            //{
            //    return OperationMode.Manual;
            //}
            //else
            //{
            //    return OperationMode.Automation;
            //}
        }

        #region Handle ProcessValue Get/Set

        private string GetEocButtonBackgroundColor()
        {
            if (_hwZoneValues == null || _hwZoneValues.EocModeIsOn == null || _hwZoneValues.EocModeIsOff == null)
                return HtmlStyleHelper.GetToggleButtonBackgroundColor(false);

            var eocModeValue = _boolProcessValues[_hwZoneValues.EocModeIsOn.Id.Guid];

            return HtmlStyleHelper.GetToggleButtonBackgroundColor(eocModeValue);
        }

        private bool GetEocValue()
        {
            if (_boolProcessValues.ContainsKey(_hwZoneValues.EocMode.Id.Guid))
            {
                return _boolProcessValues[_hwZoneValues.EocMode.Id.Guid];
            }
            return false;
        }

        private void CheckValue(IProcessValue value)
        {
            if (value.Value == null)
                return;
            if (_boolProcessValues.ContainsKey(value.ValueInfo.Id.Guid))
                _boolProcessValues[value.ValueInfo.Id.Guid] = (bool)value.Value;
            foreach (var item in _lookUpTableForHwStatus)
            {
                if (item.Value.Contains(value.ValueInfo))
                {
                    _lookUpTableForHwStatusValues[value.ValueInfo.Id] = value;
                }
            }
        }

        #endregion Handle ProcessValue Get/Set

        #region ComponentHandling

        private SequenceContentComponent SequenceContentComp = new();
        private SequenceControlGroupsComponent SequenceControlGroupsPlantComp = new();
        private SequenceControlGroupsComponent SequenceControlGroupsHwZoneComp = new();
        private GlobalOptionsComponent GlobalOptions = new();

        private void AddSubComponents()
        {
            SubComponents = new();
            SubComponents.Add(SequenceContentComp);
            SubComponents.Add(SequenceControlGroupsPlantComp);
            SubComponents.Add(SequenceControlGroupsHwZoneComp);
            SubComponents.Add(GlobalOptions);
        }

        private void UnsubscribeToAllComponents()
        {
            if (SubComponents == null) return;

            foreach (var subComponent in SubComponents)
            {
                subComponent.ComponentChangeEvent -= SubpageComponentEventTriggered;
            }
        }

        private void SubscribeToAllSubPages()
        {
            foreach (var subComponent in SubComponents)
            {
                subComponent.ComponentChangeEvent += SubpageComponentEventTriggered;
            }
        }

        private void SubpageComponentEventTriggered(object? sender, ComponentEventArgs e)
        {
            ComponentChangeEvent?.Invoke(sender, e);
        }

        private List<IBaseProcessValueComponent> SubComponents = new();

        public event EventHandler<ComponentEventArgs> ComponentChangeEvent;

        public List<IProcessValueInfo> GetValueInfos()
        {
            var currentHwzId = _projectInformationService.CurrentHardwareZone.HardwareZoneInfo.Id;
            var hwzs = _projectInformationService.HardwareZoneInfos.ToList();

            var values = _processValueInfoService.CreateHwZoneRegistration(currentHwzId, hwzs).ValueInfos;
            _hwZoneValues = _processValueInfoService.GetProcessValueInfo(currentHwzId);
            _lookUpTableForHwStatus = _processValueInfoService.GetLookUpTableForHwStatus(hwzs);

            ResetBoolValues(_hwZoneValues);

            var valueList = values.ToList();
            foreach (var subComponent in SubComponents)
            {
                if (subComponent.IsRendered)
                {
                    valueList.AddRange(subComponent.GetValueInfos());
                }
            }
            _readProcessValues = values.ToList();
            return valueList;
        }

        private List<IProcessValueInfo> _readProcessValues = new();

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

            foreach (var processValue in _readProcessValues)
            {
                if (updatedValues.ContainsKey(processValue.Id))
                {
                    CheckValue(updatedValues[processValue.Id]);
                }
            }

            foreach (var subComponent in SubComponents)
            {
                if (subComponent.IsRendered)
                {
                    subComponent.Update(updatedValues);
                }
            }

            InvokeAsync(() => StateHasChanged());
        }

        public void TriggerComponentChangeEvent(ButtonEventArgs e, IProcessValueInfo process, bool value = false)
        {
            if (e == null || process == null)
                return;
            var processValueHandleInfos = GetValueHandleInfos(process.Id, value);
            if (processValueHandleInfos.Count == 0)
                return;

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

        private List<ProcessValueHandleInfo> GetValueHandleInfos(ProcessValueId id, bool value)
        {
            var processValueHandleInfos = new List<ProcessValueHandleInfo>();
            if (id.Equals(_hwZoneValues.HardwareZoneAutomaticRelease.Id))
                processValueHandleInfos.Add(new()
                {
                    ProcessValueHandle = ProcessValueHandle.Switch,
                    ProcessValueInfo = _hwZoneValues.HardwareZoneAutomaticRelease,
                    Value = value
                });
            if (id.Equals(_hwZoneValues.EocMode.Id))
                processValueHandleInfos.Add(new()
                {
                    ProcessValueHandle = ProcessValueHandle.Switch,
                    ProcessValueInfo = _hwZoneValues.EocMode,
                    Value = !GetEocValue()
                });
            if (id.Equals(_hwZoneValues.ManualMode.Id))
            {
                processValueHandleInfos.Add(new()
                {
                    ProcessValueHandle = ProcessValueHandle.Switch,
                    ProcessValueInfo = _hwZoneValues.AutomaticMode,
                    Value = IsCurrentlyManualMode()
                });
                processValueHandleInfos.Add(new()
                {
                    ProcessValueHandle = ProcessValueHandle.Switch,
                    ProcessValueInfo = _hwZoneValues.ManualMode,
                    Value = !IsCurrentlyManualMode()
                });
            }
            return processValueHandleInfos;
        }

        #endregion ComponentHandling

        public async ValueTask DisposeAsync()
        {
            if (!_isDisposed)
            {
                _isDisposed = true;

                if (_hmiLanguageService != null)
                {
                    _hmiLanguageService.OnLanguageSet -= OnLanguageSet;
                }

                if (_resizeCallbackRef != null && _resizeCallbackRef.IsValueCreated)
                {
                    var module = await _resizeCallbackRef.Value;
                    await module.DisposeAsync();
                }

                if (_viewportReference != null)
                {
                    _viewportReference.Dispose();
                    _viewportReference = null;
                }
                foreach (var subComponent in SubComponents)
                {
                    subComponent.ComponentChangeEvent -= SubpageComponentEventTriggered;
                }
            }
        }
    }
}