using FishNet.Managing;
using FishNet.Managing.Timing;
using System;
using UnityEngine;

namespace FishNet.Connection
{

    /// <summary>
    /// A container for a connected client used to perform actions on and gather information for the declared client.
    /// </summary>
    public partial class NetworkConnection : IEquatable<NetworkConnection>
    {
#pragma warning disable CS0414
        #region Private.
        /// <summary>
        /// Last tick this connection sent a ping.
        /// </summary>
        private uint _lastPingTick;
        /// <summary>
        /// Number of times client has excessively sent a ping.
        /// </summary>
        private float _excessivePingCount;
        /// <summary>
        /// Ticks expected between each ping.
        /// </summary>
        private uint _requiredPingTicks;
        #endregion

        #region Const.
        /// <summary>
        /// Number of times a ping may occur excessively before server will punish connection.
        /// </summary>
        private const byte EXCESSIVE_PING_LIMIT = 10;
        #endregion
#pragma warning restore CS0414
        /// <summary>
        /// Initializes for ping.
        /// </summary>
        private void InitializePing()
        {
            //Give the client some room for error.
            float requiredInterval = (NetworkManager.TimeManager.PingInterval * 0.85f);
            //Round down so required ticks is lower.
            _requiredPingTicks = NetworkManager.TimeManager.TimeToTicks(requiredInterval, TickRounding.RoundDown);
        }


        /// <summary>
        /// Resets PingPong values.
        /// </summary>
        private void ResetPingPong()
        {
            _excessivePingCount = 0;
            _lastPingTick = 0;
        }

        /// <summary>
        /// Called when a ping is received from this connection. Returns if can respond to ping.
        /// </summary>
        /// <returns>True to respond to ping, false to kick connection.</returns>
        internal bool CanPingPong()
        {
            /* Only check ping conditions in build. Editors are prone to pausing which can
             * improperly kick clients. */
#if UNITY_EDITOR
            return true;
#else
            TimeManager tm = (NetworkManager == null) ? InstanceFinder.TimeManager : NetworkManager.TimeManager;
            //Server FPS is running low, timing isn't reliable enough to kick clients.
            if (tm.LowFrameRate)
                return true;

            uint currentTick = tm.Tick;
            uint difference = (currentTick - _lastPingTick);
            _lastPingTick = currentTick;

            //Ping sent too quickly.
            if (difference < _requiredPingTicks)
            {
                _excessivePingCount += 1f;
                //Ping limit hit.
                if (_excessivePingCount >= EXCESSIVE_PING_LIMIT)
                {
                    NetworkManager.LogWarning($"Kicked connectionId {ClientId} for excessive pings.");
                    Disconnect(true);
                }

                //Return to not send pong back.
                return false;
            }
            //Ping isnt too fast.
            else
            {
                _excessivePingCount = UnityEngine.Mathf.Max(0f, _excessivePingCount - 0.5f);
                return true;
            }
#endif
        }
    }


}