#if USING_NETCODE_GO

using UnityEngine;
using System.Collections.Generic;
using Unity.Netcode;
using Unity.Netcode.Components;


namespace SimplifyXR {

    /// <summary>
    /// Share animator of GameObject(s) over network. Use <cref="ModifyNetworkAuthority"/> to change if setting Client or Server authority.
    /// </summary>
    [DirectiveCategory(DirectiveCategories.Action, DirectiveSubCategory.Networking,
        prettyName = "Share Network Animator",
        directiveInfo = "This <i>action</i> shares the animator of a Networked Object based on Server or Client authority.")]
    //[ExecuteInEditMode]
    public class ShareNetworkAnimator : Actions, IChangeNetworkAuthority
    {
        /// <summary>
        /// Select if sharing for more than one Animator.
        /// </summary>
        #if UNITY_EDITOR
        [Tooltip("Select if sharing more than one Animator")]
        #endif
        public bool UseListOfAnimators;

        /// <summary>
        /// The Animator to share over the network.
        /// </summary>
        #if UNITY_EDITOR
        [Tooltip("The Animator to share over the network")]
        [Conditional("UseListOfAnimators", false, ComparisonType.Equals)]
        #endif
        public Animator Animator;

        /// <summary>
        /// The List of Animators to share over the network.
        /// </summary>
        #if UNITY_EDITOR
        [Tooltip("The List of Animators to share over the network")]
        [Conditional("UseListOfAnimators", true, ComparisonType.Equals)]
        #endif
        public List<Animator> Animators;

        /// <summary>
        /// If Network Object will be client or server authoritative
        /// </summary>
        #if UNITY_EDITOR
        [Tooltip("If network object will be client or server authoritative")]
        #endif
        public SimplifyXREnums.ClientOrServerAuthoritative NetworkAuthorityState = SimplifyXREnums.ClientOrServerAuthoritative.Client;

        public override List<KnobKeywords> ReceiveKeywords()
        {
            return new List<KnobKeywords> {
                new KnobKeywords("Animator", typeof(Animator)),
                new KnobKeywords("ListOfAnimators", typeof(List<Animator>)),
                new KnobKeywords("NetworkAuthState", typeof(SimplifyXREnums.ClientOrServerAuthoritative))
            };
        }

        public override List<KnobKeywords> SendKeywords()
        {
            return new List<KnobKeywords> {
                new KnobKeywords("Animator", typeof(Animator)),
                new KnobKeywords("ListOfAnimators", typeof(List<Animator>)),
                new KnobKeywords("NetworkAuthState", typeof(SimplifyXREnums.ClientOrServerAuthoritative))
            };
        }

        public override void Execute()
        {
            FindObjectPassed();
            if (CheckIfObjectExists())
            {
                SendData();
            }
            ThisActionCompleted();

        }

        void FindObjectPassed()
        {
            var objectPassed = GetPassableData();
            if (objectPassed == null) return;

            if (Animators.Count > 0 && UseListOfAnimators)
            {
                AddPassableData(new List<string> { "ListOfAnimators" },
                    new List<object> { Animators });
            }
            else if (Animator != null && !UseListOfAnimators)
            {
                AddPassableData(new List<string> { "Animator" },
                    new List<object> { Animator });
            }
            else if (KeywordInUse == "NetworkAuthState")
            {
                SimplifyXREnums.ClientOrServerAuthoritative clientOrServerAuthoritative = (SimplifyXREnums.ClientOrServerAuthoritative) objectPassed;
                NetworkAuthorityState = clientOrServerAuthoritative;
            }
        }

        bool CheckIfObjectExists()
        {
            if ((!UseListOfAnimators && Animator != null) || (UseListOfAnimators && Animator != null))
            {
                GetGameObject();
                if (Animators != null)
                    return true;
            }
            else
                SimplifyXRDebug.SimplifyXRLog(SimplifyXRDebug.Type.AuthorError, "No Animator specified or passed for {0}", SimplifyXRDebug.Args(this));

            return false;
        }

        void GetGameObject()
        {
            if (!UseListOfAnimators)
                ChangeGameObject(Animator);
            else
            {
                foreach (Animator a in Animators)
                    ChangeGameObject(a);
            }
        }

#if UNITY_EDITOR
        void OnValidate()
        {
            UnityEditor.EditorApplication.delayCall += () =>
            {
                if (!UseListOfAnimators)
                    ChangeGameObject(Animator);
                else
                {
                    foreach (Animator a in Animators)
                        ChangeGameObject(a);
                }
            };
        }
#endif


        void ChangeGameObject(Animator anim)
        {
            if (anim != null)
            {
                // For sending along what's been changed
                if(Animators != null)
                    Animators.Add(anim);

                PickAuthorityState(anim);
                AttachAnimator(anim);
            }
        }

        void PickAuthorityState(Animator anim)
        {
            anim.gameObject.GetOrAddComponent<NetworkObject>();

            if (NetworkAuthorityState == SimplifyXREnums.ClientOrServerAuthoritative.Client)
            {
                if (anim.gameObject.GetComponent<NetworkAnimator>() != null)
                    DestroyImmediate(anim.gameObject.GetComponent<NetworkAnimator>());

                anim.gameObject.GetOrAddComponent<OwnerNetworkAnimator>();
            }
            else if (NetworkAuthorityState == SimplifyXREnums.ClientOrServerAuthoritative.Server)
            {
                if (anim.gameObject.GetComponent<OwnerNetworkAnimator>() != null)
                    DestroyImmediate(anim.gameObject.GetComponent<OwnerNetworkAnimator>());

                anim.gameObject.GetOrAddComponent<NetworkAnimator>();
            }
        }

        void AttachAnimator(Animator anim)
        {
            anim.gameObject.GetComponent<NetworkAnimator>().Animator = anim;
        }

        void SendData()
        {
            List<object> objects;
            if (!UseListOfAnimators)
                objects = new List<object> { Animator, Animators};
            else
                objects = new List<object> { null, Animators, null };

            AddPassableData(new List<string> { "GameObject", "ListOfGameObjects", "Transform" }, objects);
        }

        #region IChangeNetworkAuthority implementation
        string IModify<IChangeNetworkAuthority>.ModifyObjectName { get { return "Client state"; } }

        public void SetClient()
        {
            NetworkAuthorityState = SimplifyXREnums.ClientOrServerAuthoritative.Client;
        }

        public void SetServer()
        {
            NetworkAuthorityState = SimplifyXREnums.ClientOrServerAuthoritative.Server;
        }
        #endregion

    }
}

#endif