using Mirror;
using UnityEngine;

namespace SanAndreasUnity.Net
{

    public class NetworkRigidBody : NetworkBehaviour
    {
        private Rigidbody _rigidbody;
        public Rigidbody Rigidbody
        {
            get => _rigidbody;
            set
            {
                if (_rigidbody == value)
                    return;

                _rigidbody = value;

                if (NetStatus.IsClientOnly)
                {
                    this.UpdateAllPropertiesOnClient();
                }
            }
        }

        [SyncVar(hook=nameof(OnNetPositionChanged))] Vector3 m_net_position = Vector3.zero;
        [SyncVar(hook=nameof(OnNetRotationChanged))] Vector3 m_net_rotation = Vector3.zero;
        [SyncVar] Vector3 m_net_velocity = Vector3.zero;
        [SyncVar] Vector3 m_net_angularVelocity = Vector3.zero;

        public bool disableCollisionDetectionOnClients = true;
        public bool disableGravityOnClients = true;


        void Awake()
        {
            this.Rigidbody = this.GetComponent<Rigidbody>();

            if (NetStatus.IsServer)
            {
                // assign syncvars before the object is spawned on network
                this.UpdateServer();
            }

            // suggest that interpolation should be changed
            if (!NetStatus.IsServer)
            {
                if (this.Rigidbody != null && this.Rigidbody.interpolation == RigidbodyInterpolation.None)
                    Debug.LogWarning($"For better sync, interpolation should be changed, rigid body: {this.Rigidbody.name}");
            }
        }

        public override void OnStartClient()
        {
            if (NetStatus.IsServer)
                return;

            // need to apply initial syncvar values, because otherwise the object may stay on the place where it
            // was originally spawned on the server (if object doesn't change position, syncvars will not be updated)

            this.UpdateAllPropertiesOnClient();
        }

        void Update()
        {
            if (NetStatus.IsServer)
                this.UpdateServer();
            else
                this.UpdateClient();
        }

        public void UpdateServer()
        {
            if (null == this.Rigidbody)
                return;

            Vector3 pos = this.Rigidbody.position;
            Vector3 rot = this.Rigidbody.rotation.eulerAngles;
            Vector3 vel = this.Rigidbody.velocity;
            Vector3 angVel = this.Rigidbody.angularVelocity;

            if (pos != m_net_position)
            {
                m_net_position = pos;
            }

            if (rot != m_net_rotation)
            {
                m_net_rotation = rot;
            }

            if (vel != m_net_velocity)
            {
                m_net_velocity = vel;
            }

            if (angVel != m_net_angularVelocity)
            {
                m_net_angularVelocity = angVel;
            }

        }

        private void UpdateClient()
        {
            if (null == this.Rigidbody)
                return;

            if (this.disableCollisionDetectionOnClients)
                this.Rigidbody.detectCollisions = false;

            if (this.disableGravityOnClients)
                this.Rigidbody.useGravity = false;

            // position and rotation are updated in syncvar hooks

            this.Rigidbody.velocity = m_net_velocity;
            this.Rigidbody.angularVelocity = m_net_angularVelocity;
        }

        private void UpdateAllPropertiesOnClient()
        {
            if (null == this.Rigidbody)
                return;

            this.Rigidbody.MovePosition(m_net_position);
            this.Rigidbody.MoveRotation(Quaternion.Euler(m_net_rotation));
            this.UpdateClient();
        }

        void OnNetPositionChanged(Vector3 pos)
        {
            if (NetStatus.IsServer)
                return;

            if (null == this.Rigidbody)
                return;

            this.Rigidbody.MovePosition(pos);
        }

        void OnNetRotationChanged(Vector3 eulers)
        {
            if (NetStatus.IsServer)
                return;

            if (null == this.Rigidbody)
                return;

            this.Rigidbody.MoveRotation(Quaternion.Euler(eulers));
        }

    }

}
