// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License

using System;
using System.Collections;
using System.Threading;
using UnityEngine.Bindings;
using UnityEngine.Scripting;
using UnityEngineInternal;
using uei = UnityEngine.Internal;

namespace UnityEngine
{
    // MonoBehaviour is the base class every script derives from.
    [RequiredByNativeCode]
    [ExtensionOfNativeClass]
    [NativeHeader("Runtime/Mono/MonoBehaviour.h")]
    [NativeHeader("Runtime/Scripting/DelayedCallUtility.h")]
    public class MonoBehaviour : Behaviour
    {
        public MonoBehaviour()
        {
            ConstructorCheck(this);
        }


        private CancellationTokenSource m_CancellationTokenSource;
        public CancellationToken destroyCancellationToken
        {
            get
            {
                if (this == null)
                    throw new MissingReferenceException("DestroyCancellation token should be called atleast once before destroying the monobehaviour object");
                if (m_CancellationTokenSource == null)
                {
                    m_CancellationTokenSource = new CancellationTokenSource();
                    OnCancellationTokenCreated();
                }
                return m_CancellationTokenSource.Token;
            }
        }

        [RequiredByNativeCode]
        private void RaiseCancellation()
        {
            m_CancellationTokenSource?.Cancel();
        }

        // Is any invoke pending on this MonoBehaviour?
        public bool IsInvoking()
        {
            return Internal_IsInvokingAll(this);
        }

        public void CancelInvoke()
        {
            Internal_CancelInvokeAll(this);
        }

        // Invokes the method /methodName/ in time seconds.
        public void Invoke(string methodName, float time)
        {
            InvokeDelayed(this, methodName, time, 0.0f);
        }

        // Invokes the method /methodName/ in /time/ seconds.
        public void InvokeRepeating(string methodName, float time, float repeatRate)
        {
            if (repeatRate <= 0.00001f && repeatRate != 0.0f)
                throw new UnityException("Invoke repeat rate has to be larger than 0.00001F)");

            InvokeDelayed(this, methodName, time, repeatRate);
        }

        // Cancels all Invoke calls with name /methodName/ on this behaviour.
        public void CancelInvoke(string methodName)
        {
            CancelInvoke(this, methodName);
        }

        // Is any invoke on /methodName/ pending?
        public bool IsInvoking(string methodName)
        {
            return IsInvoking(this, methodName);
        }

        [uei.ExcludeFromDocs]
        public Coroutine StartCoroutine(string methodName)
        {
            object value = null;
            return StartCoroutine(methodName, value);
        }

        // Starts a coroutine named /methodName/.
        public Coroutine StartCoroutine(string methodName, [uei.DefaultValue("null")] object value)
        {
            if (string.IsNullOrEmpty(methodName))
                throw new NullReferenceException("methodName is null or empty");

            if (!IsObjectMonoBehaviour(this))
                throw new ArgumentException("Coroutines can only be stopped on a MonoBehaviour");

            return StartCoroutineManaged(methodName, value);
        }

        // Starts a coroutine.
        public Coroutine StartCoroutine(IEnumerator routine)
        {
            if (routine == null)
                throw new NullReferenceException("routine is null");

            if (!IsObjectMonoBehaviour(this))
                throw new ArgumentException("Coroutines can only be stopped on a MonoBehaviour");

            return StartCoroutineManaged2(routine);
        }

        //*undocumented*
        [Obsolete("StartCoroutine_Auto has been deprecated. Use StartCoroutine instead (UnityUpgradable) -> StartCoroutine([mscorlib] System.Collections.IEnumerator)", false)]
        public Coroutine StartCoroutine_Auto(IEnumerator routine)
        {
            return StartCoroutine(routine);
        }

        // Stop a coroutine.
        public void StopCoroutine(IEnumerator routine)
        {
            if (routine == null)
                throw new NullReferenceException("routine is null");

            if (!IsObjectMonoBehaviour(this))
                throw new ArgumentException("Coroutines can only be stopped on a MonoBehaviour");

            StopCoroutineFromEnumeratorManaged(routine);
        }

        // Stop a coroutine.
        public void StopCoroutine(Coroutine routine)
        {
            if (routine == null)
                throw new NullReferenceException("routine is null");

            if (!IsObjectMonoBehaviour(this))
                throw new ArgumentException("Coroutines can only be stopped on a MonoBehaviour");

            StopCoroutineManaged(routine);
        }

        // Stops all coroutines named /methodName/ running on this behaviour.
        public extern void StopCoroutine(string methodName);

        // Stops all coroutines running on this behaviour.
        public extern void StopAllCoroutines();

        public extern bool useGUILayout { get; set; }

        // Allow a specific instance of a MonoBehaviour to run in edit mode (only available in the editor)
        public extern bool runInEditMode { get; set; }
        internal extern bool allowPrefabModeInPlayMode { get; }

        // Logs message to the Unity Console. This function is identical to [[Debug.Log]].
        public static void print(object message)
        {
            Debug.Log(message);
        }

        [NativeMethod(IsThreadSafe = true)]
        extern static void ConstructorCheck([Writable] Object self);

        [FreeFunction("CancelInvoke")]
        extern static void Internal_CancelInvokeAll([NotNull("NullExceptionObject")] MonoBehaviour self);

        [FreeFunction("IsInvoking")]
        extern static bool Internal_IsInvokingAll([NotNull("NullExceptionObject")] MonoBehaviour self);

        [FreeFunction]
        extern static void InvokeDelayed([NotNull("NullExceptionObject")] MonoBehaviour self, string methodName, float time, float repeatRate);

        [FreeFunction]
        extern static void CancelInvoke([NotNull("NullExceptionObject")] MonoBehaviour self, string methodName);

        [FreeFunction]
        extern static bool IsInvoking([NotNull("NullExceptionObject")] MonoBehaviour self, string methodName);

        [FreeFunction]
        extern static bool IsObjectMonoBehaviour([NotNull("NullExceptionObject")] Object obj);

        extern Coroutine StartCoroutineManaged(string methodName, object value);

        extern Coroutine StartCoroutineManaged2(IEnumerator enumerator);

        extern void StopCoroutineManaged(Coroutine routine);

        extern void StopCoroutineFromEnumeratorManaged(IEnumerator routine);

        extern internal string GetScriptClassName();

        extern void OnCancellationTokenCreated();
    }
}
