using System;
using FrameworkEngine.Debugging;
using FrameworkEngine.Locate;
using UnityEngine;


namespace GameEngine
{
	[RequireComponent(typeof(Rigidbody2D))]
	[RequireComponent(typeof(CircleCollider2D))]
	public class EntityMover : MonoBehaviour, ILocateBehaviour
	{
		private const float _MAX_GROUND_HEIGHT = 20f;
		private static readonly Quaternion _Rotate90 = Quaternion.Euler(0f, 0f, -90f);
		[SerializeField] private float m_Speed = 5;
		[SerializeField] private int m_MaxSteps = 5;
		[SerializeField] private float m_Dampen = 8f;
		[SerializeField] private float m_PushOut = 0.1f;
		[SerializeField] private LayerMask m_GroundLayer = default(LayerMask);
		[SerializeField] private CategoryLogger m_Logger = default(CategoryLogger);
		[SerializeField] private bool m_IgnoreRotation = default(bool);
		private Rigidbody2D _rigidBody;
		private CircleCollider2D _collider;




		public float Speed
		{
			get { return m_Speed; }
			set { m_Speed = value; }
		}




		private void Awake()
		{
			_rigidBody = GetComponent<Rigidbody2D>();
			_collider = GetComponent<CircleCollider2D>();
		}

		
		private void FixedUpdate()
		{
			Vector2 center = _rigidBody.position + _collider.offset;
			Vector2 castPosition = center;
			castPosition.y = _MAX_GROUND_HEIGHT;
			
			RaycastHit2D groundTest = Physics2D.CircleCast(castPosition, _collider.radius, Vector2.down, _MAX_GROUND_HEIGHT * 2f, m_GroundLayer);

			Vector2 normal;
			Vector2 direction;
			Vector2 movePoint;
			float remainingDistance = m_Speed * Time.deltaTime;

			if (groundTest.transform != null)
			{
				int steps = 0;

				normal = groundTest.normal;
				center = groundTest.point + groundTest.normal * _collider.radius + _collider.offset;
				direction = _Rotate90 * normal;
				movePoint = _rigidBody.position;

				RaycastHit2D hit = Physics2D.CircleCast(center, _collider.radius, direction, remainingDistance, m_GroundLayer);

				while (steps < m_MaxSteps &&
					   hit.collider != null)
				{
					Vector2 offset = hit.distance * direction;
					movePoint += offset;
					center += offset;
					remainingDistance -= hit.distance;
					normal = hit.normal;
					direction = _Rotate90 * normal;
					hit = Physics2D.CircleCast(center, _collider.radius, direction + normal * m_PushOut, remainingDistance, m_GroundLayer);
					steps++;
				}
			}
			else
			{
				movePoint = _rigidBody.position;
				direction = Vector2.right;
				normal = Vector2.up;
			}
			
			Quaternion currentRotation = Quaternion.Euler(0f, 0f, _rigidBody.rotation);
			Quaternion targetRotation = Quaternion.LookRotation(Vector3.forward, normal);
		
			_rigidBody.MovePosition(movePoint + remainingDistance*direction);

			if (m_IgnoreRotation)
			{
				_rigidBody.MoveRotation(0f);
			}
			else
			{
				_rigidBody.MoveRotation(Quaternion.Lerp(currentRotation, targetRotation, m_Dampen * Time.deltaTime).eulerAngles.z);
			}
		
			//ForceToGround();
		}
		



		private void ForceToGround()
		{
			Vector2 center = _rigidBody.position + _collider.offset;
			Vector2 castPosition = center;
			castPosition.y = _MAX_GROUND_HEIGHT;
			RaycastHit2D groundTest = Physics2D.CircleCast(castPosition, _collider.radius, Vector2.down, _MAX_GROUND_HEIGHT * 2f, m_GroundLayer);

			_rigidBody.position = groundTest.point + groundTest.normal * _collider.radius - _collider.offset;
		}


		public void SetDistance(float distance)
		{
			_rigidBody.transform.position = new Vector3(distance, 0f, 0f);
			ForceToGround();
		}
	}
}
