#if USING_NETCODE_GO

using UnityEngine;
using System.Linq;
using System.Collections.Generic;
#if USING_XRI
using UnityEngine.XR.Interaction.Toolkit;
#endif
using Unity.Netcode;
using Unity.Netcode.Components;
using System;
using System.Diagnostics.Tracing;


namespace SimplifyXR {

    /// <summary>
    /// Change ownership of a network object over the network.
    /// </summary>
    [DirectiveCategory(DirectiveCategories.Action, DirectiveSubCategory.Networking,
        prettyName = "Change Ownership Network Object",
        directiveInfo = "This <i>action</i> changes the ownership of a Networked Object based on Server Authority.")]
    //[ExecuteInEditMode]
    public class ChangeOwnershipNetworkObject : UseListOrSingleObject, IChangeWhatGameObjectExecutes, IChangeNetworkAuthority
    {
        /// <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;

        ChangeOwnershipNetworkBehaviour changeOwnershipNetworkBehaviour;
        NetworkObject no;

        public override List<KnobKeywords> ReceiveKeywords()
        {
            return new List<KnobKeywords> {
                new KnobKeywords("GameObject", typeof(GameObject)),
                new KnobKeywords("Transform", typeof(Transform)),
                new KnobKeywords("ListOfGameObjects", typeof(List<GameObject>)),
                new KnobKeywords("NetworkObject", typeof(NetworkObject)),
                new KnobKeywords("ListOfNetworkObject", typeof(List<NetworkObject>)),
#if USING_XRI
                new KnobKeywords("BaseInteractionEventArgs", typeof(BaseInteractionEventArgs)),
#endif
                new KnobKeywords("NetworkAuthState", typeof(SimplifyXREnums.ClientOrServerAuthoritative)),
            };
        }

        public override List<KnobKeywords> SendKeywords()
        {
            return new List<KnobKeywords> {
                new KnobKeywords("GameObject", typeof(GameObject)),
                new KnobKeywords("Transform", typeof(Transform)),
                new KnobKeywords("ListOfGameObjects", typeof(List<GameObject>)),
                new KnobKeywords("NetworkObject", typeof(NetworkObject)),
                new KnobKeywords("ListOfNetworkObject", typeof(List<NetworkObject>)),
#if USING_XRI
                new KnobKeywords("BaseInteractionEventArgs", typeof(BaseInteractionEventArgs)),
#endif
                new KnobKeywords("NetworkAuthState", typeof(SimplifyXREnums.ClientOrServerAuthoritative)),
            };
        }

        public override void Execute()
        {
            ResetPassedObjects();
            FindObjectPassed();

            if (!UseListOfObjects)
                ChangeOwnershipGameObject(ObjectToChange);
            else
            {
                foreach (GameObject g in ObjectsToChange)
                    ChangeOwnershipGameObject(g);
            }

            if (CheckIfObjectExists())
            {
                SendData();
            }
            ThisActionCompleted();

        }

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

            if (KeywordInUse == "GameObject")
            {
                ObjectToChange = objectPassed as GameObject;
                UseListOfObjects = false;
            }
            else if (KeywordInUse == "Transform")
            {
                var passedTansform = objectPassed as Transform;
                ObjectToChange = passedTansform.gameObject;
                UseListOfObjects = false;
            }
            else if (KeywordInUse == "ListOfGameObjects")
            {
                var newList = objectPassed as List<GameObject>;
                ObjectsToChange = new List<GameObject>(newList);
                UseListOfObjects = true;
            }
            else if (KeywordInUse == "NetworkObject")
            {
                var passedNetworkObject = objectPassed as NetworkObject;
                ObjectToChange = passedNetworkObject.gameObject;
                UseListOfObjects = false;
            }
            else if (KeywordInUse == "ListOfNetworkObjects")
            {
                var newList = objectPassed as List<NetworkObject>;
                for(int i = 0; i<newList.Count; i++)
                {
                    ObjectsToChange.Add(newList[i].gameObject);
                }
                UseListOfObjects = true;
            }
#if USING_XRI
            else if (KeywordInUse == "BaseInteractionEventArgs")
            {
                var passedBaseInteractionEventArgs = objectPassed as BaseInteractionEventArgs;
                ObjectToChange = passedBaseInteractionEventArgs.interactableObject.transform.gameObject;
                UseListOfObjects = false;
            }
#endif
            else if (KeywordInUse == "NetworkAuthState")
            {
                SimplifyXREnums.ClientOrServerAuthoritative clientOrServerAuthoritative = (SimplifyXREnums.ClientOrServerAuthoritative)objectPassed;
                NetworkAuthorityState = clientOrServerAuthoritative;
            }
        }

        bool CheckIfObjectExists()
        {
            if ((!UseListOfObjects && ObjectToChange != null) || (UseListOfObjects && ObjectsToChange != null && ObjectsToChange.Any()))
            {
                GetGameObject();
                if (gameObjectsToChange != null)
                    return true;
            }
            else
                SimplifyXRDebug.SimplifyXRLog(SimplifyXRDebug.Type.AuthorError, "No GameObject specified or passed for {0}", SimplifyXRDebug.Args(this));

            return false;
        }

        void GetGameObject()
        {
            if (!UseListOfObjects)
                ChangeGameObject(ObjectToChange);
            else
            {
                foreach (GameObject g in ObjectsToChange)
                    ChangeGameObject(g);
            }
        }

        void ChangeGameObject(GameObject go)
        {
            if (go != null)
            {
                // For sending along what's been changed
                if(gameObjectsToChange != null)
                    gameObjectsToChange.Add(go);
            }
        }

        void ChangeOwnershipGameObject(GameObject go)
        {
            changeOwnershipNetworkBehaviour = go.GetOrAddComponent<ChangeOwnershipNetworkBehaviour>();
            no = go.GetOrAddComponent<NetworkObject>();

            if (NetworkAuthorityState == SimplifyXREnums.ClientOrServerAuthoritative.Client)
            {
                if (no != null)
                {
                    if (Unity.Netcode.NetworkManager.Singleton.IsServer)// && IsOwner
                    {
                        //NetworkManager was taken over by SimplifyXR.NetworkManager? Im assuming to use other networking libraries.
                        no.ChangeOwnership(Unity.Netcode.NetworkManager.Singleton.LocalClient.ClientId);
                    }
                    else
                    {
                        if (Unity.Netcode.NetworkManager.Singleton.LocalClient != null)
                        {
                            changeOwnershipNetworkBehaviour.RequestOwnershipServerRpc(Unity.Netcode.NetworkManager.Singleton.LocalClient.ClientId, no);
                        }
                    }
                }
            }
            else if (NetworkAuthorityState == SimplifyXREnums.ClientOrServerAuthoritative.Server)
            {
                if (no != null)
                {
                    // Can only be called by the server
                    changeOwnershipNetworkBehaviour.RequestRemoveOwnershipServerRpc(no);
                }
            }
        }

        void SendData()
        {
            List<object> objects;
            if (!UseListOfObjects)
                objects = new List<object> { ObjectToChange, gameObjectsToChange, ObjectToChange.transform };
            else
                objects = new List<object> { null, gameObjectsToChange, null };

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

#region IChangeWhatGameObjectExecutes implementation
        string IModify<IChangeWhatGameObjectExecutes>.ModifyObjectName { get { return "Object to change"; } }

        public void ChangeWhatGameObjectExecutes(GameObject newObject)
        {
            ObjectToChange = newObject;
        }
#endregion

#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