/*
 * Copyright (c) 2024 chocopoi
 * 
 * This file is part of DressingTools.
 * 
 * DressingTools is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * DressingTools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with DressingTools. If not, see <https://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Chocopoi.DressingFramework.Localization;
using Chocopoi.DressingTools.Components.Animations;
using Chocopoi.DressingTools.Localization;
using Chocopoi.DressingTools.UI.Presenters;
using Chocopoi.DressingTools.UI.Views;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace Chocopoi.DressingTools.Inspector.Views
{
    [ExcludeFromCodeCoverage]
    internal class ParameterSlotView : ElementView, IParameterSlotView
    {
        private static readonly I18nTranslator t = I18n.ToolTranslator;

        public event Action ConfigChanged;
        public event Action<DTSmartControl> AddMapping;

        public DTParameterSlot Target { get; set; }
        public int ValueType { get => _valueTypePopupField.index; set => _valueTypePopupField.index = value; }
        public List<ParameterSlotMapping> Mappings { get; set; }
        public bool ShowAvatarRootNotFoundHelpbox { get; set; }
        public bool ShowNoDefaultValueMappingHelpbox { get; set; }
        public bool ShowDuplicateMappingsHelpbox { get; set; }
        public float ParameterDefaultValue
        {
            get => _defValFloatField.value;
            set
            {
                _defValFloatField.value = value;
                _defValIntField.value = (int)value;
            }
        }

        private readonly ParameterSlotPresenter _presenter;
        private PopupField<string> _valueTypePopupField;
        private FloatField _defValFloatField;
        private IntegerField _defValIntField;
        private VisualElement _mappingsHelpboxContainer;
        private VisualElement _mappingsContainer;
        private VisualElement _mappingsAddFieldContainer;
        private readonly bool _showDescriptionHelpbox;
        private readonly DTSmartControl _currentCtrl;

        public ParameterSlotView(DTSmartControl currentCtrl = null, bool showDescriptionHelpbox = false)
        {
            _presenter = new ParameterSlotPresenter(this);
            Mappings = new List<ParameterSlotMapping>();
            _currentCtrl = currentCtrl;
            _showDescriptionHelpbox = showDescriptionHelpbox;
        }

        private void InitVisualTree()
        {
            var tree = Resources.Load<VisualTreeAsset>("ParameterSlotView");
            tree.CloneTree(this);
            var styleSheet = Resources.Load<StyleSheet>("ParameterSlotViewStyles");
            if (!styleSheets.Contains(styleSheet))
            {
                styleSheets.Add(styleSheet);
            }
        }

        private void InitHelpboxes()
        {
            var helpboxContainer = Q<VisualElement>("description-helpbox-container").First();
            if (_showDescriptionHelpbox)
            {
                helpboxContainer.Add(CreateHelpBox(t._("inspector.parameterSlot.helpbox.description"), UnityEditor.MessageType.Info));
            }
            helpboxContainer.Add(CreateHelpBox(t._("inspector.parameterSlot.helpbox.emptyParameterAutoGenerated"), UnityEditor.MessageType.Info));
        }

        private void UpdateValueTypeDisplay()
        {
            var intType = ValueType == 0;
            _defValIntField.style.display = intType ? DisplayStyle.Flex : DisplayStyle.None;
            _defValFloatField.style.display = intType ? DisplayStyle.None : DisplayStyle.Flex;
        }

        private void InitValueType()
        {
            var popupContainer = Q<VisualElement>("value-type-popup-container").First();
            var choices = new List<string>() { "Int", "Float" };
            _valueTypePopupField = new PopupField<string>(t._("inspector.parameterSlot.popup.valueType"), choices, 0);
            _valueTypePopupField.RegisterValueChangedCallback(evt =>
            {
                UpdateValueTypeDisplay();
                ConfigChanged?.Invoke();
            });
            popupContainer.Add(_valueTypePopupField);
        }

        private void InitDefValFields()
        {
            _defValFloatField = Q<FloatField>("parameter-defval-float-field").First();
            _defValFloatField.RegisterValueChangedCallback(evt =>
            {
                _defValIntField.value = (int)_defValFloatField.value;
                ConfigChanged?.Invoke();
            });
            _defValIntField = Q<IntegerField>("parameter-defval-int-field").First();
            _defValIntField.RegisterValueChangedCallback(evt =>
            {
                _defValFloatField.value = _defValIntField.value;
                ConfigChanged?.Invoke();
            });
        }

        private void InitMappingsContainer()
        {
            _mappingsHelpboxContainer = Q<VisualElement>("mappings-helpbox-container").First();
            _mappingsContainer = Q<VisualElement>("mappings-container").First();
        }

        private void InitMappingAddField()
        {
            _mappingsAddFieldContainer = Q<VisualElement>("mappings-add-field-container").First();
            _mappingsAddFieldContainer.AddToClassList("add-field-container");
            var label = new Label("+");
            _mappingsAddFieldContainer.Add(label);
            var objAddField = new ObjectField()
            {
                objectType = typeof(DTSmartControl)
            };
            objAddField.RegisterValueChangedCallback((evt) =>
            {
                if (objAddField.value != null)
                {
                    AddMapping?.Invoke((DTSmartControl)objAddField.value);
                    objAddField.value = null;
                }
            });
            _mappingsAddFieldContainer.Add(objAddField);
        }

        private VisualElement MakeMappingEntry(DTSmartControl ctrl, Action<float> onChange, Action onRemove)
        {
            var isCurrentCtrl = _currentCtrl != null && _currentCtrl == ctrl;

            var elem = new VisualElement();

            elem.AddToClassList("object-field-entry");

            if (isCurrentCtrl)
            {
                // var label = new Label("*");
                elem.style.backgroundColor = new StyleColor(new Color(0.3f, 0.6f, 0.0f));
                // elem.Add(label);
            }

            if (ValueType == 0)
            {
                var field = new IntegerField()
                {
                    value = (int)ctrl.ParameterSlotConfig.MappedValue
                };
                field.RegisterValueChangedCallback(evt => onChange?.Invoke(field.value));
                elem.Add(field);
            }
            else if (ValueType == 1)
            {
                var field = new FloatField()
                {
                    value = ctrl.ParameterSlotConfig.MappedValue
                };
                field.RegisterValueChangedCallback(evt => onChange?.Invoke(field.value));
                elem.Add(field);
            }

            var objField = new ObjectField()
            {
                objectType = typeof(DTSmartControl),
                value = ctrl,
            };
            objField.SetEnabled(false);
            elem.Add(objField);

            if (!isCurrentCtrl)
            {
                // only display remove if not current ctrl
                var rmvBtn = new Button(onRemove)
                {
                    text = "x"
                };
                elem.Add(rmvBtn);
            }

            return elem;
        }

        public void RepaintMappingHelpboxes()
        {
            _mappingsHelpboxContainer.Clear();
            if (ShowAvatarRootNotFoundHelpbox)
            {
                _mappingsHelpboxContainer.Add(CreateHelpBox(t._("inspector.parameterSlot.helpbox.avatarRootNotFound"), UnityEditor.MessageType.Error));
                _mappingsAddFieldContainer.style.display = DisplayStyle.None;
                return;
            }
            _mappingsAddFieldContainer.style.display = DisplayStyle.Flex;
            if (ShowNoDefaultValueMappingHelpbox)
            {
                _mappingsHelpboxContainer.Add(CreateHelpBox(t._("inspector.parameterSlot.helpbox.noDefaultValueMapping"), UnityEditor.MessageType.Warning));
            }
            if (ShowDuplicateMappingsHelpbox)
            {
                _mappingsHelpboxContainer.Add(CreateHelpBox(t._("inspector.parameterSlot.helpbox.duplicateMappings"), UnityEditor.MessageType.Warning));
            }
        }

        private void RepaintMappings()
        {
            RepaintMappingHelpboxes();
            _mappingsContainer.Clear();
            if (ShowAvatarRootNotFoundHelpbox)
            {
                return;
            }
            foreach (var mapping in Mappings)
            {
                _mappingsContainer.Add(MakeMappingEntry(mapping.ctrl, mapping.onChange, mapping.onRemove));
            }
        }

        public override void OnEnable()
        {
            InitVisualTree();
            InitHelpboxes();
            InitValueType();
            InitDefValFields();
            InitMappingsContainer();
            InitMappingAddField();

            t.LocalizeElement(this);

            RaiseLoadEvent();
        }

        public override void OnDisable()
        {
            base.OnDisable();
        }

        public override void Repaint()
        {
            UpdateValueTypeDisplay();
            RepaintMappings();
        }
    }
}
