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

        public override void Execute()
        {
            ResetPassedObjects();
            FindObjectPassed();
            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
        }

        void OnValidate()
        {
#if UNITY_EDITOR
            UnityEditor.EditorApplication.delayCall += () =>
            {
                if (!UseListOfObjects)
                    ChangeGameObject(ObjectToChange);
                else
                {
                    foreach (GameObject g in ObjectsToChange)
                        ChangeGameObject(g);
                }
            };
#endif
        }

        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);

                SetupOwnershipNetworkBehaviour(go);
            }
        }

        void SetupOwnershipNetworkBehaviour(GameObject go)
        {

            if (!go.GetComponent<ChangeOwnershipNetworkBehaviour>())
                go.AddComponent<ChangeOwnershipNetworkBehaviour>();

            if (!go.GetComponent<NetworkObject>())
                go.AddComponent<NetworkObject>();
        }

        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
    }
}

#endif