using System;
using System.Linq;

namespace Assets.Source.UI
{

    public abstract class UIElement<T> : UIElementBase where T : IUIElementProperties
    {
        public override event Action OnOpenStart;
        public override event Action OnCloseStart;
        public override event Action OnOpenEnd;
        public override event Action OnCloseEnd;

        public event Action OnNextCloseStart;
        public event Action OnNextOpenStart;
        public event Action OnNextOpenEnd;
        public event Action OnNextCloseEnd;

        public T Properties;

        public UIAnimation[] UIAnimations; // is set automatically from OnApplyPrefab
        public float DefaultOpenDuration = 0.5f;
        public float DefaultCloseDuration = 0.5f;

        public UIElementBase[] SubElementPrefabs;
        
        protected virtual void OnPropertiesSet()
        { }
        
        public sealed override void Open(IUIElementProperties properties = null, bool instantly = false)
        {
            if (State == UIElementState.Opened || State == UIElementState.TransitionOpen)
                return;

            ConnectedElements.Clear();

            gameObject.SetActive(true);
            Properties = (T) properties;

            if (instantly)
                OnOpenTransitionEnd();
            else
            {
                State = UIElementState.TransitionOpen;
                this.After(GetOpenDuration(), OnOpenTransitionEnd);
            }

            foreach (var uiAnimation in UIAnimations)
            {
                uiAnimation.Trigger(UIAnimationTrigger.Open);
                if (instantly)
                    uiAnimation.EndInstantly();
            }

            foreach (var subElementPrefab in SubElementPrefabs)
            {
                var e = Get.MainUI.Open(subElementPrefab.GetType(), subElementPrefab.ExplicitID);
                e.AddConnectedElement(this);
                AddConnectedElement(e);
            }

            OnOpenStart?.Invoke();

            OnNextOpenStart?.Invoke();
            OnNextOpenStart = null;

            VOpen(properties, instantly);
        }
        
        public sealed override void Close(bool instantly = false)
        {
            if (State == UIElementState.Closed || State == UIElementState.TransitionClose)
                return;

            if (instantly)
                OnCloseTransitionEnd();
            else
            {
                State = UIElementState.TransitionClose;
                this.After(GetCloseDuration(), OnCloseTransitionEnd);
            }

            foreach (var uiAnimation in UIAnimations)
            {
                uiAnimation.Trigger(UIAnimationTrigger.Close);
                if (instantly)
                    uiAnimation.EndInstantly();
            }

            foreach (var e in ConnectedElements)
                e.Close(instantly);

            OnCloseStart?.Invoke();

            OnNextCloseStart?.Invoke();
            OnNextCloseStart = null;

            VClose(instantly);
        }

#if UNITY_EDITOR
        public void OnApplyPrefab()
        {
            UIAnimations = GetComponentsInChildren<UIAnimation>();
        }
#endif

        protected virtual void VOpen(IUIElementProperties properties, bool instantly) { }
        protected virtual void VClose(bool instantly) { }

        private void OnOpenTransitionEnd()
        {
            State = UIElementState.Opened;
            OnOpenEnd?.Invoke();
            OnNextOpenEnd?.Invoke();
            OnNextOpenEnd = null;
        }

        private void OnCloseTransitionEnd()
        {
            State = UIElementState.Closed;
            OnCloseEnd?.Invoke();
            OnNextCloseEnd?.Invoke();
            OnNextCloseEnd = null;
            gameObject.SetActive(false);
        }

        public float GetOpenDuration()
        {
            var dur = DefaultOpenDuration;
            if (UIAnimations.Length > 0)
                dur = UIAnimations.Max(uiAnimation => uiAnimation.GetInDuration());
            return dur;
        }

        public float GetCloseDuration()
        {
            var dur = DefaultCloseDuration;
            if (UIAnimations.Length > 0)
                dur = UIAnimations.Max(uiAnimation => uiAnimation.GetOutDuration());
            return dur;
        }
    }
}