#if FISHNET_THREADED_TICKSMOOTHERS
using FishNet.Managing.Timing;
using FishNet.Object;
using GameKit.Dependencies.Utilities;
using UnityEngine;
using Unity.Profiling;

namespace FishNet.Component.Transforming.Beta
{
    /// <summary>
    /// Smoothes this object between ticks.
    /// </summary>
    /// <remarks>This can be configured to smooth over a set interval of time, or to smooth adaptively and make path corrections for prediction.</remarks>
    public class TickSmootherController : IResettable
    {
        #region Public.
        // /// <summary>
        // /// Logic for owner smoothing.
        // /// </summary>
        // public UniversalTickSmoother UniversalSmoother { get; private set; }
        #endregion
        
        #region Private.
        
        #region Private Profiler Markers
        
        private static readonly ProfilerMarker _pm_OnUpdate = new("TickSmootherController.TimeManager_OnUpdate()");
        private static readonly ProfilerMarker _pm_OnPreTick = new("TickSmootherController.TimeManager_OnPreTick()");
        private static readonly ProfilerMarker _pm_OnPostTick = new("TickSmootherController.TimeManager_OnPostTick()");
        
        #endregion
        
        /// <summary>
        /// </summary>
        private InitializationSettings _initializationSettings = new();
        /// <summary>
        /// </summary>
        private MovementSettings _ownerMovementSettings = new();
        /// <summary>
        /// </summary>
        private MovementSettings _spectatorMovementSettings = new();
        /// <summary>
        /// True if OnDestroy has been called.
        /// </summary>
        private bool _destroyed;
        /// <summary>
        /// Cached timeManager reference.
        /// </summary>
        private TimeManager _timeManager;
        /// <summary>
        /// NetworkBehaviour which initialized this object. Value may be null when initialized for an Offline smoother.
        /// </summary>
        private NetworkBehaviour _initializingNetworkBehaviour;
        /// <summary>
        /// TickSmoothingManager.
        /// </summary>
        private TickSmoothingManager _tickSmoothingManager;
        /// <summary>
        /// Transform which initialized this object.
        /// </summary>
        private Transform _graphicalTransform;
        /// <summary>
        /// True if initialized with a null NetworkBehaviour.
        /// </summary>
        private bool _initializedOffline;
        /// <summary>
        /// True if subscribed to events used for adaptiveInterpolation.
        /// </summary>
        private bool _subscribedToAdaptiveEvents;
        /// <summary>
        /// True if currently subscribed to events.
        /// </summary>
        private bool _subscribed;
        /// <summary>
        /// True if initialized.
        /// </summary>
        private bool _isInitialized;
        #endregion

        public void Initialize(InitializationSettings initializationSettings, MovementSettings ownerSettings, MovementSettings spectatorSettings)
        {
            _tickSmoothingManager =
                initializationSettings.InitializingNetworkBehaviour?.NetworkManager.TickSmoothingManager ??
                InstanceFinder.NetworkManager.TickSmoothingManager;
            _initializingNetworkBehaviour = initializationSettings.InitializingNetworkBehaviour;
            _graphicalTransform = initializationSettings.GraphicalTransform;

            _initializationSettings = initializationSettings;
            _ownerMovementSettings = ownerSettings;
            _spectatorMovementSettings = spectatorSettings;

            _initializedOffline = initializationSettings.InitializingNetworkBehaviour == null;

            _isInitialized = true;
        }

        public void OnDestroy()
        {
            TickSmoothingManager tsm = _tickSmoothingManager;
            if (tsm != null)
                tsm.Unregister(this);
            
            // ChangeSubscriptions(false);
            // StoreSmoother();
            _destroyed = true;
            _isInitialized = false;
        }

        public void StartSmoother()
        {
            if (!_isInitialized)
                return;

            bool canStart = _initializedOffline ? StartOffline() : StartOnline();

            if (!canStart)
                return;

            // RetrieveSmoothers();
            //
            // UniversalSmoother.Initialize(_initializationSettings, _ownerMovementSettings, _spectatorMovementSettings);
            //
            // UniversalSmoother.StartSmoother();

            TickSmoothingManager tsm = _tickSmoothingManager;
            if (tsm != null)
                tsm.Register(this, _initializationSettings, _ownerMovementSettings, _spectatorMovementSettings);

            bool StartOnline()
            {
                NetworkBehaviour nb = _initializingNetworkBehaviour;

                SetTimeManager(nb.TimeManager);

                return true;
            }

            bool StartOffline()
            {
                if (_timeManager == null)
                    return false;

                return true;
            }
        }

        public void StopSmoother()
        {
            TickSmoothingManager tsm = _tickSmoothingManager;
            if (tsm != null)
                tsm.Unregister(this);
            
            if (!_initializedOffline)
                StopOnline();
            
            // if (UniversalSmoother != null)
            //     UniversalSmoother.StopSmoother();

            void StopOnline()
            {
                SetTimeManager(tm: null);
            }

            // Intentionally left blank.
            // void StopOffline() { }
        }

        // public void TimeManager_OnUpdate()
        // {
        //     using (_pm_OnUpdate.Auto())
        //     {
        //         UniversalSmoother.OnUpdate(Time.deltaTime);
        //     }
        // }
        //
        // public void TimeManager_OnPreTick()
        // {
        //     using (_pm_OnPreTick.Auto())
        //     {
        //         UniversalSmoother.OnPreTick();
        //     }
        // }
        //
        // /// <summary>
        // /// Called after a tick completes.
        // /// </summary>
        // public void TimeManager_OnPostTick()
        // {
        //     using (_pm_OnPostTick.Auto())
        //     {
        //         if (_timeManager != null)
        //             UniversalSmoother.OnPostTick(_timeManager.LocalTick);
        //     }
        // }
        //
        // private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick)
        // {
        //     UniversalSmoother.OnPostReplicateReplay(clientTick);
        // }
        //
        // private void TimeManager_OnRoundTripTimeUpdated(long rttMs)
        // {
        //     UniversalSmoother.UpdateRealtimeInterpolation();
        // }
        //
        // /// <summary>
        // /// Stores smoothers if they have value.
        // /// </summary>
        // private void StoreSmoother()
        // {
        //     if (UniversalSmoother == null)
        //         return;
        //
        //     ResettableObjectCaches<UniversalTickSmoother>.Store(UniversalSmoother);
        //     UniversalSmoother = null;
        // }
        //
        // /// <summary>
        // /// Stores current smoothers and retrieves new ones.
        // /// </summary>
        // private void RetrieveSmoothers()
        // {
        //     StoreSmoother();
        //     UniversalSmoother = ResettableObjectCaches<UniversalTickSmoother>.Retrieve();
        // }

        // /// <summary>
        // /// Sets a target transform to follow.
        // /// </summary>
        // public void SetTargetTransform(Transform value)
        // {
        //     Transform currentTargetTransform = _initializationSettings.TargetTransform;
        //
        //     if (value == currentTargetTransform)
        //         return;
        //
        //     bool clientStartCalled = (_initializedOffline && _timeManager != null) || (_initializingNetworkBehaviour != null && _initializingNetworkBehaviour.OnStartClientCalled);
        //
        //     bool previousTargetTransformIsValid = (currentTargetTransform != null);
        //
        //     // If target is different and old is not null then reset.
        //     if (previousTargetTransformIsValid && clientStartCalled)
        //         OnStopClient();
        //
        //     _initializationSettings.TargetTransform = value;
        //     if (previousTargetTransformIsValid && clientStartCalled)
        //         OnStartClient();
        // }

        /// <summary>
        /// Sets a new PredictionManager to use.
        /// </summary>
        public void SetTimeManager(TimeManager tm)
        {
            if (tm == _timeManager)
                return;

            // Unsub from current.
            // ChangeSubscriptions(false);
            //Sub to newest.
            _timeManager = tm;
            // ChangeSubscriptions(true);
        }
        
        
        // /// <summary>
        // /// Changes the subscription to the TimeManager.
        // /// </summary>
        // private void ChangeSubscriptions(bool subscribe)
        // {
        //     if (_destroyed)
        //         return;
        //     TimeManager tm = _timeManager;
        //     if (tm == null)
        //         return;
        //
        //     if (subscribe == _subscribed)
        //         return;
        //     _subscribed = subscribe;
        //
        //     bool adaptiveIsOff = _ownerMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off && _spectatorMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off;
        //
        //     if (subscribe)
        //     {
        //         tm.OnUpdate += TimeManager_OnUpdate;
        //         tm.OnPreTick += TimeManager_OnPreTick;
        //         tm.OnPostTick += TimeManager_OnPostTick;
        //
        //         if (!adaptiveIsOff)
        //         {
        //             tm.OnRoundTripTimeUpdated += TimeManager_OnRoundTripTimeUpdated;
        //             PredictionManager pm = tm.NetworkManager.PredictionManager;
        //             pm.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay;
        //             _subscribedToAdaptiveEvents = true;
        //         }
        //     }
        //     else
        //     {
        //         tm.OnUpdate -= TimeManager_OnUpdate;
        //         tm.OnPreTick -= TimeManager_OnPreTick;
        //         tm.OnPostTick -= TimeManager_OnPostTick;
        //
        //         if (_subscribedToAdaptiveEvents)
        //         {
        //             tm.OnRoundTripTimeUpdated -= TimeManager_OnRoundTripTimeUpdated;
        //             PredictionManager pm = tm.NetworkManager.PredictionManager;
        //             pm.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay;
        //         }
        //     }
        // }
        
        public void ResetState()
        {
            _initializationSettings = default;
            _ownerMovementSettings = default;
            _spectatorMovementSettings = default;

            _destroyed = false;
            _timeManager = null;
            _initializingNetworkBehaviour = null;
            _graphicalTransform = null;

            _subscribed = false;
            _subscribedToAdaptiveEvents = false;

            _isInitialized = false;
        }

        public void InitializeState() { }
    }
}
#endif